[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/Kconfig b/src/kernel/linux/v4.14/drivers/iio/magnetometer/Kconfig
new file mode 100644
index 0000000..ed9d776
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/Kconfig
@@ -0,0 +1,178 @@
+#
+# Magnetometer sensors
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Magnetometer sensors"
+
+config AK8974
+	tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
+	depends on I2C
+	depends on OF
+	select REGMAP_I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for Asahi Kasei AK8974, AMI305 or
+	  AMI306 I2C-based 3-axis magnetometer chips.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ak8974.
+
+config AK8975
+	tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	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
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
+config BMC150_MAGN_I2C
+	tristate "Bosch BMC150 I2C Magnetometer Driver"
+	depends on I2C
+	select BMC150_MAGN
+	select REGMAP_I2C
+	help
+	  Say yes here to build support for the BMC150 magnetometer with
+	  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.
+
+	  This driver also supports I2C Bosch BMC156 and BMM150 chips.
+	  To compile this driver as a module, choose M here: the module will be
+	  called bmc150_magn_i2c.
+
+config BMC150_MAGN_SPI
+	tristate "Bosch BMC150 SPI Magnetometer Driver"
+	depends on SPI
+	select BMC150_MAGN
+	select REGMAP_SPI
+	help
+	  Say yes here to build support for the BMC150 magnetometer with
+	  SPI 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.
+
+	  This driver also supports SPI Bosch BMC156 and BMM150 chips.
+	  To compile this driver as a module, choose M here: the module will be
+	  called bmc150_magn_spi.
+
+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
+
+config SENSORS_HMC5843
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
+config SENSORS_HMC5843_I2C
+	tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer (I2C)"
+	depends on I2C
+	select SENSORS_HMC5843
+	select REGMAP_I2C
+	help
+	  Say Y here to add support for the Honeywell HMC5843, HMC5883 and
+	  HMC5883L 3-Axis Magnetometer (digital compass).
+
+	  This driver can also be compiled as a set of modules.
+	  If so, these modules will be created:
+	  - hmc5843_core (core functions)
+	  - hmc5843_i2c (support for HMC5843, HMC5883, HMC5883L and HMC5983)
+
+config SENSORS_HMC5843_SPI
+	tristate "Honeywell HMC5983 3-Axis Magnetometer (SPI)"
+	depends on SPI_MASTER
+	select SENSORS_HMC5843
+	select REGMAP_SPI
+	help
+	  Say Y here to add support for the Honeywell HMC5983 3-Axis Magnetometer
+	  (digital compass).
+
+	  This driver can also be compiled as a set of modules.
+	  If so, these modules will be created:
+	  - hmc5843_core (core functions)
+	  - hmc5843_spi (support for HMC5983)
+
+endmenu
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/Makefile b/src/kernel/linux/v4.14/drivers/iio/magnetometer/Makefile
new file mode 100644
index 0000000..664b2f8
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for industrial I/O Magnetometer sensor drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AK8974)	+= ak8974.o
+obj-$(CONFIG_AK8975)	+= ak8975.o
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
+obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
+obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.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
+
+obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
+obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
+obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8974.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8974.c
new file mode 100644
index 0000000..11b9cc4
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8974.c
@@ -0,0 +1,960 @@
+/*
+ * Driver for the Asahi Kasei EMD Corporation AK8974
+ * and Aichi Steel AMI305 magnetometer chips.
+ * Based on a patch from Samu Onkalo and the AK8975 IIO driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Samu Onkalo <samu.p.onkalo@nokia.com>
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h> /* For irq_get_irq_data() */
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/*
+ * 16-bit registers are little-endian. LSB is at the address defined below
+ * and MSB is at the next higher address.
+ */
+
+/* These registers are common for AK8974 and AMI30x */
+#define AK8974_SELFTEST		0x0C
+#define AK8974_SELFTEST_IDLE	0x55
+#define AK8974_SELFTEST_OK	0xAA
+
+#define AK8974_INFO		0x0D
+
+#define AK8974_WHOAMI		0x0F
+#define AK8974_WHOAMI_VALUE_AMI306 0x46
+#define AK8974_WHOAMI_VALUE_AMI305 0x47
+#define AK8974_WHOAMI_VALUE_AK8974 0x48
+
+#define AK8974_DATA_X		0x10
+#define AK8974_DATA_Y		0x12
+#define AK8974_DATA_Z		0x14
+#define AK8974_INT_SRC		0x16
+#define AK8974_STATUS		0x18
+#define AK8974_INT_CLEAR	0x1A
+#define AK8974_CTRL1		0x1B
+#define AK8974_CTRL2		0x1C
+#define AK8974_CTRL3		0x1D
+#define AK8974_INT_CTRL		0x1E
+#define AK8974_INT_THRES	0x26  /* Absolute any axis value threshold */
+#define AK8974_PRESET		0x30
+
+/* AK8974-specific offsets */
+#define AK8974_OFFSET_X		0x20
+#define AK8974_OFFSET_Y		0x22
+#define AK8974_OFFSET_Z		0x24
+/* AMI305-specific offsets */
+#define AMI305_OFFSET_X		0x6C
+#define AMI305_OFFSET_Y		0x72
+#define AMI305_OFFSET_Z		0x78
+
+/* Different temperature registers */
+#define AK8974_TEMP		0x31
+#define AMI305_TEMP		0x60
+
+/* AMI306-specific control register */
+#define AMI306_CTRL4		0x5C
+
+/* AMI306 factory calibration data */
+
+/* fine axis sensitivity */
+#define AMI306_FINEOUTPUT_X	0x90
+#define AMI306_FINEOUTPUT_Y	0x92
+#define AMI306_FINEOUTPUT_Z	0x94
+
+/* axis sensitivity */
+#define AMI306_SENS_X		0x96
+#define AMI306_SENS_Y		0x98
+#define AMI306_SENS_Z		0x9A
+
+/* axis cross-interference */
+#define AMI306_GAIN_PARA_XZ	0x9C
+#define AMI306_GAIN_PARA_XY	0x9D
+#define AMI306_GAIN_PARA_YZ	0x9E
+#define AMI306_GAIN_PARA_YX	0x9F
+#define AMI306_GAIN_PARA_ZY	0xA0
+#define AMI306_GAIN_PARA_ZX	0xA1
+
+/* offset at ZERO magnetic field */
+#define AMI306_OFFZERO_X	0xF8
+#define AMI306_OFFZERO_Y	0xFA
+#define AMI306_OFFZERO_Z	0xFC
+
+
+#define AK8974_INT_X_HIGH	BIT(7) /* Axis over +threshold  */
+#define AK8974_INT_Y_HIGH	BIT(6)
+#define AK8974_INT_Z_HIGH	BIT(5)
+#define AK8974_INT_X_LOW	BIT(4) /* Axis below -threshold	*/
+#define AK8974_INT_Y_LOW	BIT(3)
+#define AK8974_INT_Z_LOW	BIT(2)
+#define AK8974_INT_RANGE	BIT(1) /* Range overflow (any axis) */
+
+#define AK8974_STATUS_DRDY	BIT(6) /* Data ready */
+#define AK8974_STATUS_OVERRUN	BIT(5) /* Data overrun */
+#define AK8974_STATUS_INT	BIT(4) /* Interrupt occurred */
+
+#define AK8974_CTRL1_POWER	BIT(7) /* 0 = standby; 1 = active */
+#define AK8974_CTRL1_RATE	BIT(4) /* 0 = 10 Hz; 1 = 20 Hz	 */
+#define AK8974_CTRL1_FORCE_EN	BIT(1) /* 0 = normal; 1 = force	 */
+#define AK8974_CTRL1_MODE2	BIT(0) /* 0 */
+
+#define AK8974_CTRL2_INT_EN	BIT(4)  /* 1 = enable interrupts	      */
+#define AK8974_CTRL2_DRDY_EN	BIT(3)  /* 1 = enable data ready signal */
+#define AK8974_CTRL2_DRDY_POL	BIT(2)  /* 1 = data ready active high   */
+#define AK8974_CTRL2_RESDEF	(AK8974_CTRL2_DRDY_POL)
+
+#define AK8974_CTRL3_RESET	BIT(7) /* Software reset		  */
+#define AK8974_CTRL3_FORCE	BIT(6) /* Start forced measurement */
+#define AK8974_CTRL3_SELFTEST	BIT(4) /* Set selftest register	  */
+#define AK8974_CTRL3_RESDEF	0x00
+
+#define AK8974_INT_CTRL_XEN	BIT(7) /* Enable interrupt for this axis */
+#define AK8974_INT_CTRL_YEN	BIT(6)
+#define AK8974_INT_CTRL_ZEN	BIT(5)
+#define AK8974_INT_CTRL_XYZEN	(BIT(7)|BIT(6)|BIT(5))
+#define AK8974_INT_CTRL_POL	BIT(3) /* 0 = active low; 1 = active high */
+#define AK8974_INT_CTRL_PULSE	BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
+#define AK8974_INT_CTRL_RESDEF	(AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
+
+/* The AMI305 has elaborate FW version and serial number registers */
+#define AMI305_VER		0xE8
+#define AMI305_SN		0xEA
+
+#define AK8974_MAX_RANGE	2048
+
+#define AK8974_POWERON_DELAY	50
+#define AK8974_ACTIVATE_DELAY	1
+#define AK8974_SELFTEST_DELAY	1
+/*
+ * Set the autosuspend to two orders of magnitude larger than the poweron
+ * delay to make sane reasonable power tradeoff savings (5 seconds in
+ * this case).
+ */
+#define AK8974_AUTOSUSPEND_DELAY 5000
+
+#define AK8974_MEASTIME		3
+
+#define AK8974_PWR_ON		1
+#define AK8974_PWR_OFF		0
+
+/**
+ * struct ak8974 - state container for the AK8974 driver
+ * @i2c: parent I2C client
+ * @orientation: mounting matrix, flipped axis etc
+ * @map: regmap to access the AK8974 registers over I2C
+ * @regs: the avdd and dvdd power regulators
+ * @name: the name of the part
+ * @variant: the whoami ID value (for selecting code paths)
+ * @lock: locks the magnetometer for exclusive use during a measurement
+ * @drdy_irq: uses the DRDY IRQ line
+ * @drdy_complete: completion for DRDY
+ * @drdy_active_low: the DRDY IRQ is active low
+ */
+struct ak8974 {
+	struct i2c_client *i2c;
+	struct iio_mount_matrix orientation;
+	struct regmap *map;
+	struct regulator_bulk_data regs[2];
+	const char *name;
+	u8 variant;
+	struct mutex lock;
+	bool drdy_irq;
+	struct completion drdy_complete;
+	bool drdy_active_low;
+	/* Ensure timestamp is naturally aligned */
+	struct {
+		__le16 channels[3];
+		s64 ts __aligned(8);
+	} scan;
+};
+
+static const char ak8974_reg_avdd[] = "avdd";
+static const char ak8974_reg_dvdd[] = "dvdd";
+
+static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
+{
+	int ret;
+	__le16 bulk;
+
+	ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
+	if (ret)
+		return ret;
+	*val = le16_to_cpu(bulk);
+
+	return 0;
+}
+
+static int ak8974_set_u16_val(struct ak8974 *ak8974, u8 reg, u16 val)
+{
+	__le16 bulk = cpu_to_le16(val);
+
+	return regmap_bulk_write(ak8974->map, reg, &bulk, 2);
+}
+
+static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
+{
+	int ret;
+	u8 val;
+
+	val = mode ? AK8974_CTRL1_POWER : 0;
+	val |= AK8974_CTRL1_FORCE_EN;
+	ret = regmap_write(ak8974->map, AK8974_CTRL1, val);
+	if (ret < 0)
+		return ret;
+
+	if (mode)
+		msleep(AK8974_ACTIVATE_DELAY);
+
+	return 0;
+}
+
+static int ak8974_reset(struct ak8974 *ak8974)
+{
+	int ret;
+
+	/* Power on to get register access. Sets CTRL1 reg to reset state */
+	ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+	if (ret)
+		return ret;
+	ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF);
+	if (ret)
+		return ret;
+	ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
+	if (ret)
+		return ret;
+	ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
+			   AK8974_INT_CTRL_RESDEF);
+	if (ret)
+		return ret;
+
+	/* After reset, power off is default state */
+	return ak8974_set_power(ak8974, AK8974_PWR_OFF);
+}
+
+static int ak8974_configure(struct ak8974 *ak8974)
+{
+	int ret;
+
+	ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
+			   AK8974_CTRL2_INT_EN);
+	if (ret)
+		return ret;
+	ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
+	if (ret)
+		return ret;
+	if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI306) {
+		/* magic from datasheet: set high-speed measurement mode */
+		ret = ak8974_set_u16_val(ak8974, AMI306_CTRL4, 0xA07E);
+		if (ret)
+			return ret;
+	}
+	ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
+	if (ret)
+		return ret;
+
+	return regmap_write(ak8974->map, AK8974_PRESET, 0);
+}
+
+static int ak8974_trigmeas(struct ak8974 *ak8974)
+{
+	unsigned int clear;
+	u8 mask;
+	u8 val;
+	int ret;
+
+	/* Clear any previous measurement overflow status */
+	ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear);
+	if (ret)
+		return ret;
+
+	/* If we have a DRDY IRQ line, use it */
+	if (ak8974->drdy_irq) {
+		mask = AK8974_CTRL2_INT_EN |
+			AK8974_CTRL2_DRDY_EN |
+			AK8974_CTRL2_DRDY_POL;
+		val = AK8974_CTRL2_DRDY_EN;
+
+		if (!ak8974->drdy_active_low)
+			val |= AK8974_CTRL2_DRDY_POL;
+
+		init_completion(&ak8974->drdy_complete);
+		ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
+					 mask, val);
+		if (ret)
+			return ret;
+	}
+
+	/* Force a measurement */
+	return regmap_update_bits(ak8974->map,
+				  AK8974_CTRL3,
+				  AK8974_CTRL3_FORCE,
+				  AK8974_CTRL3_FORCE);
+}
+
+static int ak8974_await_drdy(struct ak8974 *ak8974)
+{
+	int timeout = 2;
+	unsigned int val;
+	int ret;
+
+	if (ak8974->drdy_irq) {
+		ret = wait_for_completion_timeout(&ak8974->drdy_complete,
+					1 + msecs_to_jiffies(1000));
+		if (!ret) {
+			dev_err(&ak8974->i2c->dev,
+				"timeout waiting for DRDY IRQ\n");
+			return -ETIMEDOUT;
+		}
+		return 0;
+	}
+
+	/* Default delay-based poll loop */
+	do {
+		msleep(AK8974_MEASTIME);
+		ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+		if (ret < 0)
+			return ret;
+		if (val & AK8974_STATUS_DRDY)
+			return 0;
+	} while (--timeout);
+
+	dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n");
+	return -ETIMEDOUT;
+}
+
+static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
+{
+	unsigned int src;
+	int ret;
+
+	ret = ak8974_await_drdy(ak8974);
+	if (ret)
+		return ret;
+	ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src);
+	if (ret < 0)
+		return ret;
+
+	/* Out of range overflow! Strong magnet close? */
+	if (src & AK8974_INT_RANGE) {
+		dev_err(&ak8974->i2c->dev,
+			"range overflow in sensor\n");
+		return -ERANGE;
+	}
+
+	ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static irqreturn_t ak8974_drdy_irq(int irq, void *d)
+{
+	struct ak8974 *ak8974 = d;
+
+	if (!ak8974->drdy_irq)
+		return IRQ_NONE;
+
+	/* TODO: timestamp here to get good measurement stamps */
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d)
+{
+	struct ak8974 *ak8974 = d;
+	unsigned int val;
+	int ret;
+
+	/* Check if this was a DRDY from us */
+	ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+	if (ret < 0) {
+		dev_err(&ak8974->i2c->dev, "error reading DRDY status\n");
+		return IRQ_HANDLED;
+	}
+	if (val & AK8974_STATUS_DRDY) {
+		/* Yes this was our IRQ */
+		complete(&ak8974->drdy_complete);
+		return IRQ_HANDLED;
+	}
+
+	/* We may be on a shared IRQ, let the next client check */
+	return IRQ_NONE;
+}
+
+static int ak8974_selftest(struct ak8974 *ak8974)
+{
+	struct device *dev = &ak8974->i2c->dev;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+	if (ret)
+		return ret;
+	if (val != AK8974_SELFTEST_IDLE) {
+		dev_err(dev, "selftest not idle before test\n");
+		return -EIO;
+	}
+
+	/* Trigger self-test */
+	ret = regmap_update_bits(ak8974->map,
+			AK8974_CTRL3,
+			AK8974_CTRL3_SELFTEST,
+			AK8974_CTRL3_SELFTEST);
+	if (ret) {
+		dev_err(dev, "could not write CTRL3\n");
+		return ret;
+	}
+
+	msleep(AK8974_SELFTEST_DELAY);
+
+	ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+	if (ret)
+		return ret;
+	if (val != AK8974_SELFTEST_OK) {
+		dev_err(dev, "selftest result NOT OK (%02x)\n", val);
+		return -EIO;
+	}
+
+	ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+	if (ret)
+		return ret;
+	if (val != AK8974_SELFTEST_IDLE) {
+		dev_err(dev, "selftest not idle after test (%02x)\n", val);
+		return -EIO;
+	}
+	dev_dbg(dev, "passed self-test\n");
+
+	return 0;
+}
+
+static void ak8974_read_calib_data(struct ak8974 *ak8974, unsigned int reg,
+				   __le16 *tab, size_t tab_size)
+{
+	int ret = regmap_bulk_read(ak8974->map, reg, tab, tab_size);
+	if (ret) {
+		memset(tab, 0xFF, tab_size);
+		dev_warn(&ak8974->i2c->dev,
+			 "can't read calibration data (regs %u..%zu): %d\n",
+			 reg, reg + tab_size - 1, ret);
+	} else {
+		add_device_randomness(tab, tab_size);
+	}
+}
+
+static int ak8974_detect(struct ak8974 *ak8974)
+{
+	unsigned int whoami;
+	const char *name;
+	int ret;
+	unsigned int fw;
+	u16 sn;
+
+	ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami);
+	if (ret)
+		return ret;
+
+	name = "ami305";
+
+	switch (whoami) {
+	case AK8974_WHOAMI_VALUE_AMI306:
+		name = "ami306";
+		/* fall-through */
+	case AK8974_WHOAMI_VALUE_AMI305:
+		ret = regmap_read(ak8974->map, AMI305_VER, &fw);
+		if (ret)
+			return ret;
+		fw &= 0x7f; /* only bits 0 thru 6 valid */
+		ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn);
+		if (ret)
+			return ret;
+		add_device_randomness(&sn, sizeof(sn));
+		dev_info(&ak8974->i2c->dev,
+			 "detected %s, FW ver %02x, S/N: %04x\n",
+			 name, fw, sn);
+		break;
+	case AK8974_WHOAMI_VALUE_AK8974:
+		name = "ak8974";
+		dev_info(&ak8974->i2c->dev, "detected AK8974\n");
+		break;
+	default:
+		dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
+			whoami);
+		return -ENODEV;
+	}
+
+	ak8974->name = name;
+	ak8974->variant = whoami;
+
+	if (whoami == AK8974_WHOAMI_VALUE_AMI306) {
+		__le16 fab_data1[9], fab_data2[3];
+		int i;
+
+		ak8974_read_calib_data(ak8974, AMI306_FINEOUTPUT_X,
+				       fab_data1, sizeof(fab_data1));
+		ak8974_read_calib_data(ak8974, AMI306_OFFZERO_X,
+				       fab_data2, sizeof(fab_data2));
+
+		for (i = 0; i < 3; ++i) {
+			static const char axis[3] = "XYZ";
+			static const char pgaxis[6] = "ZYZXYX";
+			unsigned offz = le16_to_cpu(fab_data2[i]) & 0x7F;
+			unsigned fine = le16_to_cpu(fab_data1[i]);
+			unsigned sens = le16_to_cpu(fab_data1[i + 3]);
+			unsigned pgain1 = le16_to_cpu(fab_data1[i + 6]);
+			unsigned pgain2 = pgain1 >> 8;
+
+			pgain1 &= 0xFF;
+
+			dev_info(&ak8974->i2c->dev,
+				 "factory calibration for axis %c: offz=%u sens=%u fine=%u pga%c=%u pga%c=%u\n",
+				 axis[i], offz, sens, fine, pgaxis[i * 2],
+				 pgain1, pgaxis[i * 2 + 1], pgain2);
+		}
+	}
+
+	return 0;
+}
+
+static int ak8974_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2,
+			   long mask)
+{
+	struct ak8974 *ak8974 = iio_priv(indio_dev);
+	__le16 hw_values[3];
+	int ret = -EINVAL;
+
+	pm_runtime_get_sync(&ak8974->i2c->dev);
+	mutex_lock(&ak8974->lock);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (chan->address > 2) {
+			dev_err(&ak8974->i2c->dev, "faulty channel address\n");
+			ret = -EIO;
+			goto out_unlock;
+		}
+		ret = ak8974_trigmeas(ak8974);
+		if (ret)
+			goto out_unlock;
+		ret = ak8974_getresult(ak8974, hw_values);
+		if (ret)
+			goto out_unlock;
+
+		/*
+		 * We read all axes and discard all but one, for optimized
+		 * reading, use the triggered buffer.
+		 */
+		*val = (s16)le16_to_cpu(hw_values[chan->address]);
+
+		ret = IIO_VAL_INT;
+	}
+
+ out_unlock:
+	mutex_unlock(&ak8974->lock);
+	pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+	pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+
+	return ret;
+}
+
+static void ak8974_fill_buffer(struct iio_dev *indio_dev)
+{
+	struct ak8974 *ak8974 = iio_priv(indio_dev);
+	int ret;
+
+	pm_runtime_get_sync(&ak8974->i2c->dev);
+	mutex_lock(&ak8974->lock);
+
+	ret = ak8974_trigmeas(ak8974);
+	if (ret) {
+		dev_err(&ak8974->i2c->dev, "error triggering measure\n");
+		goto out_unlock;
+	}
+	ret = ak8974_getresult(ak8974, ak8974->scan.channels);
+	if (ret) {
+		dev_err(&ak8974->i2c->dev, "error getting measures\n");
+		goto out_unlock;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &ak8974->scan,
+					   iio_get_time_ns(indio_dev));
+
+ out_unlock:
+	mutex_unlock(&ak8974->lock);
+	pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+	pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+}
+
+static irqreturn_t ak8974_handle_trigger(int irq, void *p)
+{
+	const struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+
+	ak8974_fill_buffer(indio_dev);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_mount_matrix *
+ak8974_get_mount_matrix(const struct iio_dev *indio_dev,
+			const struct iio_chan_spec *chan)
+{
+	struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+	return &ak8974->orientation;
+}
+
+static const struct iio_chan_spec_ext_info ak8974_ext_info[] = {
+	IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix),
+	{ },
+};
+
+#define AK8974_AXIS_CHANNEL(axis, index)				\
+	{								\
+		.type = IIO_MAGN,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+		.ext_info = ak8974_ext_info,				\
+		.address = index,					\
+		.scan_index = index,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_LE				\
+		},							\
+	}
+
+static const struct iio_chan_spec ak8974_channels[] = {
+	AK8974_AXIS_CHANNEL(X, 0),
+	AK8974_AXIS_CHANNEL(Y, 1),
+	AK8974_AXIS_CHANNEL(Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long ak8974_scan_masks[] = { 0x7, 0 };
+
+static const struct iio_info ak8974_info = {
+	.read_raw = &ak8974_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
+{
+	struct i2c_client *i2c = to_i2c_client(dev);
+	struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+	struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+	switch (reg) {
+	case AK8974_CTRL1:
+	case AK8974_CTRL2:
+	case AK8974_CTRL3:
+	case AK8974_INT_CTRL:
+	case AK8974_INT_THRES:
+	case AK8974_INT_THRES + 1:
+	case AK8974_PRESET:
+	case AK8974_PRESET + 1:
+		return true;
+	case AK8974_OFFSET_X:
+	case AK8974_OFFSET_X + 1:
+	case AK8974_OFFSET_Y:
+	case AK8974_OFFSET_Y + 1:
+	case AK8974_OFFSET_Z:
+	case AK8974_OFFSET_Z + 1:
+		if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
+			return true;
+		return false;
+	case AMI305_OFFSET_X:
+	case AMI305_OFFSET_X + 1:
+	case AMI305_OFFSET_Y:
+	case AMI305_OFFSET_Y + 1:
+	case AMI305_OFFSET_Z:
+	case AMI305_OFFSET_Z + 1:
+		return ak8974->variant == AK8974_WHOAMI_VALUE_AMI305 ||
+		       ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
+	case AMI306_CTRL4:
+	case AMI306_CTRL4 + 1:
+		return ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
+	default:
+		return false;
+	}
+}
+
+static bool ak8974_precious_reg(struct device *dev, unsigned int reg)
+{
+	return reg == AK8974_INT_CLEAR;
+}
+
+static const struct regmap_config ak8974_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.writeable_reg = ak8974_writeable_reg,
+	.precious_reg = ak8974_precious_reg,
+};
+
+static int ak8974_probe(struct i2c_client *i2c,
+			const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct ak8974 *ak8974;
+	unsigned long irq_trig;
+	int irq = i2c->irq;
+	int ret;
+
+	/* Register with IIO */
+	indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	ak8974 = iio_priv(indio_dev);
+	i2c_set_clientdata(i2c, indio_dev);
+	ak8974->i2c = i2c;
+	mutex_init(&ak8974->lock);
+
+	ret = of_iio_read_mount_matrix(&i2c->dev,
+				       "mount-matrix",
+				       &ak8974->orientation);
+	if (ret)
+		return ret;
+
+	ak8974->regs[0].supply = ak8974_reg_avdd;
+	ak8974->regs[1].supply = ak8974_reg_dvdd;
+
+	ret = devm_regulator_bulk_get(&i2c->dev,
+				      ARRAY_SIZE(ak8974->regs),
+				      ak8974->regs);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "cannot get regulators\n");
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "cannot enable regulators\n");
+		return ret;
+	}
+
+	/* Take runtime PM online */
+	pm_runtime_get_noresume(&i2c->dev);
+	pm_runtime_set_active(&i2c->dev);
+	pm_runtime_enable(&i2c->dev);
+
+	ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config);
+	if (IS_ERR(ak8974->map)) {
+		dev_err(&i2c->dev, "failed to allocate register map\n");
+		pm_runtime_put_noidle(&i2c->dev);
+		pm_runtime_disable(&i2c->dev);
+		return PTR_ERR(ak8974->map);
+	}
+
+	ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+	if (ret) {
+		dev_err(&i2c->dev, "could not power on\n");
+		goto disable_pm;
+	}
+
+	ret = ak8974_detect(ak8974);
+	if (ret) {
+		dev_err(&i2c->dev, "neither AK8974 nor AMI30x found\n");
+		goto disable_pm;
+	}
+
+	ret = ak8974_selftest(ak8974);
+	if (ret)
+		dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
+
+	ret = ak8974_reset(ak8974);
+	if (ret) {
+		dev_err(&i2c->dev, "AK8974 reset failed\n");
+		goto disable_pm;
+	}
+
+	indio_dev->dev.parent = &i2c->dev;
+	indio_dev->channels = ak8974_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
+	indio_dev->info = &ak8974_info;
+	indio_dev->available_scan_masks = ak8974_scan_masks;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = ak8974->name;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 ak8974_handle_trigger,
+					 NULL);
+	if (ret) {
+		dev_err(&i2c->dev, "triggered buffer setup failed\n");
+		goto disable_pm;
+	}
+
+	/* If we have a valid DRDY IRQ, make use of it */
+	if (irq > 0) {
+		irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+		if (irq_trig == IRQF_TRIGGER_RISING) {
+			dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n");
+		} else if (irq_trig == IRQF_TRIGGER_FALLING) {
+			ak8974->drdy_active_low = true;
+			dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n");
+		} else {
+			irq_trig = IRQF_TRIGGER_RISING;
+		}
+		irq_trig |= IRQF_ONESHOT;
+		irq_trig |= IRQF_SHARED;
+
+		ret = devm_request_threaded_irq(&i2c->dev,
+						irq,
+						ak8974_drdy_irq,
+						ak8974_drdy_irq_thread,
+						irq_trig,
+						ak8974->name,
+						ak8974);
+		if (ret) {
+			dev_err(&i2c->dev, "unable to request DRDY IRQ "
+				"- proceeding without IRQ\n");
+			goto no_irq;
+		}
+		ak8974->drdy_irq = true;
+	}
+
+no_irq:
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&i2c->dev, "device register failed\n");
+		goto cleanup_buffer;
+	}
+
+	pm_runtime_set_autosuspend_delay(&i2c->dev,
+					 AK8974_AUTOSUSPEND_DELAY);
+	pm_runtime_use_autosuspend(&i2c->dev);
+	pm_runtime_put(&i2c->dev);
+
+	return 0;
+
+cleanup_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+disable_pm:
+	pm_runtime_put_noidle(&i2c->dev);
+	pm_runtime_disable(&i2c->dev);
+	ak8974_set_power(ak8974, AK8974_PWR_OFF);
+	regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+	return ret;
+}
+
+static int ak8974_remove(struct i2c_client *i2c)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+	struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	pm_runtime_get_sync(&i2c->dev);
+	pm_runtime_put_noidle(&i2c->dev);
+	pm_runtime_disable(&i2c->dev);
+	ak8974_set_power(ak8974, AK8974_PWR_OFF);
+	regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+	return 0;
+}
+
+static int __maybe_unused ak8974_runtime_suspend(struct device *dev)
+{
+	struct ak8974 *ak8974 =
+		iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+	ak8974_set_power(ak8974, AK8974_PWR_OFF);
+	regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+	return 0;
+}
+
+static int __maybe_unused ak8974_runtime_resume(struct device *dev)
+{
+	struct ak8974 *ak8974 =
+		iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+	if (ret)
+		return ret;
+	msleep(AK8974_POWERON_DELAY);
+	ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+	if (ret)
+		goto out_regulator_disable;
+
+	ret = ak8974_configure(ak8974);
+	if (ret)
+		goto out_disable_power;
+
+	return 0;
+
+out_disable_power:
+	ak8974_set_power(ak8974, AK8974_PWR_OFF);
+out_regulator_disable:
+	regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+	return ret;
+}
+
+static const struct dev_pm_ops ak8974_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(ak8974_runtime_suspend,
+			   ak8974_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id ak8974_id[] = {
+	{"ami305", 0 },
+	{"ami306", 0 },
+	{"ak8974", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ak8974_id);
+
+static const struct of_device_id ak8974_of_match[] = {
+	{ .compatible = "asahi-kasei,ak8974", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ak8974_of_match);
+
+static struct i2c_driver ak8974_driver = {
+	.driver	 = {
+		.name	= "ak8974",
+		.pm = &ak8974_dev_pm_ops,
+		.of_match_table = of_match_ptr(ak8974_of_match),
+	},
+	.probe	  = ak8974_probe,
+	.remove	  = ak8974_remove,
+	.id_table = ak8974_id,
+};
+module_i2c_driver(ak8974_driver);
+
+MODULE_DESCRIPTION("AK8974 and AMI30x 3-axis magnetometer driver");
+MODULE_AUTHOR("Samu Onkalo");
+MODULE_AUTHOR("Linus Walleij");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8975.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8975.c
new file mode 100644
index 0000000..b08bd99
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/ak8975.c
@@ -0,0 +1,1120 @@
+/*
+ * 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/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/magnetometer/ak8975.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 const 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;
+	const struct ak_def	*def;
+	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;
+	struct iio_mount_matrix orientation;
+	struct regulator	*vdd;
+	struct regulator	*vid;
+
+	/* Ensure natural alignment of timestamp */
+	struct {
+		s16 channels[3];
+		s64 ts __aligned(8);
+	} scan;
+};
+
+/* Enable attached power regulator if any. */
+static int ak8975_power_on(const struct ak8975_data *data)
+{
+	int ret;
+
+	ret = regulator_enable(data->vdd);
+	if (ret) {
+		dev_warn(&data->client->dev,
+			 "Failed to enable specified Vdd supply\n");
+		return ret;
+	}
+	ret = regulator_enable(data->vid);
+	if (ret) {
+		dev_warn(&data->client->dev,
+			 "Failed to enable specified Vid supply\n");
+		return ret;
+	}
+	/*
+	 * According to the datasheet the power supply rise time i 200us
+	 * and the minimum wait time before mode setting is 100us, in
+	 * total 300 us. Add some margin and say minimum 500us here.
+	 */
+	usleep_range(500, 1000);
+	return 0;
+}
+
+/* Disable attached power regulator if any. */
+static void ak8975_power_off(const struct ak8975_data *data)
+{
+	regulator_disable(data->vid);
+	regulator_disable(data->vdd);
+}
+
+/*
+ * 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_or_emulated(
+			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_or_emulated(
+			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;
+}
+
+static int ak8975_start_read_axis(struct ak8975_data *data,
+				  const struct i2c_client *client)
+{
+	/* Set up the device for taking a sample. */
+	int ret = ak8975_set_mode(data, MODE_ONCE);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting operating mode\n");
+		return ret;
+	}
+
+	/* 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)
+		return ret;
+
+	/* 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");
+			return ret;
+		}
+		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);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/* Retrieve raw flux value for one of 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);
+	const struct i2c_client *client = data->client;
+	const struct ak_def *def = data->def;
+	__le16 rval;
+	u16 buff;
+	int ret;
+
+	pm_runtime_get_sync(&data->client->dev);
+
+	mutex_lock(&data->lock);
+
+	ret = ak8975_start_read_axis(data, client);
+	if (ret)
+		goto exit;
+
+	ret = i2c_smbus_read_i2c_block_data_or_emulated(
+			client, def->data_regs[index],
+			sizeof(rval), (u8*)&rval);
+	if (ret < 0)
+		goto exit;
+
+	mutex_unlock(&data->lock);
+
+	pm_runtime_mark_last_busy(&data->client->dev);
+	pm_runtime_put_autosuspend(&data->client->dev);
+
+	/* Swap bytes and convert to valid range. */
+	buff = le16_to_cpu(rval);
+	*val = clamp_t(s16, buff, -def->range, def->range);
+	return IIO_VAL_INT;
+
+exit:
+	mutex_unlock(&data->lock);
+	dev_err(&client->dev, "Error in reading axis\n");
+	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;
+}
+
+static const struct iio_mount_matrix *
+ak8975_get_mount_matrix(const struct iio_dev *indio_dev,
+			const struct iio_chan_spec *chan)
+{
+	return &((struct ak8975_data *)iio_priv(indio_dev))->orientation;
+}
+
+static const struct iio_chan_spec_ext_info ak8975_ext_info[] = {
+	IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8975_get_mount_matrix),
+	{ },
+};
+
+#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,					\
+		.scan_index = index,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_CPU				\
+		},							\
+		.ext_info = ak8975_ext_info,				\
+	}
+
+static const struct iio_chan_spec ak8975_channels[] = {
+	AK8975_CHANNEL(X, 0), AK8975_CHANNEL(Y, 1), AK8975_CHANNEL(Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long ak8975_scan_masks[] = { 0x7, 0 };
+
+static const struct iio_info ak8975_info = {
+	.read_raw = &ak8975_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#ifdef CONFIG_ACPI
+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);
+#endif
+
+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 void ak8975_fill_buffer(struct iio_dev *indio_dev)
+{
+	struct ak8975_data *data = iio_priv(indio_dev);
+	const struct i2c_client *client = data->client;
+	const struct ak_def *def = data->def;
+	int ret;
+	__le16 fval[3];
+
+	mutex_lock(&data->lock);
+
+	ret = ak8975_start_read_axis(data, client);
+	if (ret)
+		goto unlock;
+
+	/*
+	 * For each axis, read the flux value from the appropriate register
+	 * (the register is specified in the iio device attributes).
+	 */
+	ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
+							def->data_regs[0],
+							3 * sizeof(fval[0]),
+							(u8 *)fval);
+	if (ret < 0)
+		goto unlock;
+
+	mutex_unlock(&data->lock);
+
+	/* Clamp to valid range. */
+	data->scan.channels[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
+	data->scan.channels[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
+	data->scan.channels[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
+					   iio_get_time_ns(indio_dev));
+
+	return;
+
+unlock:
+	mutex_unlock(&data->lock);
+	dev_err(&client->dev, "Error in reading axes block\n");
+}
+
+static irqreturn_t ak8975_handle_trigger(int irq, void *p)
+{
+	const struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+
+	ak8975_fill_buffer(indio_dev);
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+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;
+	const struct ak8975_platform_data *pdata =
+		dev_get_platdata(&client->dev);
+
+	/* Grab and set up the supplied GPIO. */
+	if (pdata)
+		eoc_gpio = pdata->eoc_gpio;
+	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;
+
+	if (!pdata) {
+		err = of_iio_read_mount_matrix(&client->dev,
+					       "mount-matrix",
+					       &data->orientation);
+		if (err)
+			return err;
+	} else
+		data->orientation = pdata->orientation;
+
+	/* 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);
+		if (!name)
+			return -ENODEV;
+	} 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];
+
+	/* Fetch the regulators */
+	data->vdd = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(data->vdd))
+		return PTR_ERR(data->vdd);
+	data->vid = devm_regulator_get(&client->dev, "vid");
+	if (IS_ERR(data->vid))
+		return PTR_ERR(data->vid);
+
+	err = ak8975_power_on(data);
+	if (err)
+		return err;
+
+	err = ak8975_who_i_am(client, data->def->type);
+	if (err < 0) {
+		dev_err(&client->dev, "Unexpected device\n");
+		goto power_off;
+	}
+	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);
+		goto power_off;
+	}
+
+	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->available_scan_masks = ak8975_scan_masks;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = name;
+
+	err = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger,
+					 NULL);
+	if (err) {
+		dev_err(&client->dev, "triggered buffer setup failed\n");
+		goto power_off;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err) {
+		dev_err(&client->dev, "device register failed\n");
+		goto cleanup_buffer;
+	}
+
+	/* Enable runtime PM */
+	pm_runtime_get_noresume(&client->dev);
+	pm_runtime_set_active(&client->dev);
+	pm_runtime_enable(&client->dev);
+	/*
+	 * The device comes online in 500us, so add two orders of magnitude
+	 * of delay before autosuspending: 50 ms.
+	 */
+	pm_runtime_set_autosuspend_delay(&client->dev, 50);
+	pm_runtime_use_autosuspend(&client->dev);
+	pm_runtime_put(&client->dev);
+
+	return 0;
+
+cleanup_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+power_off:
+	ak8975_power_off(data);
+	return err;
+}
+
+static int ak8975_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ak8975_data *data = iio_priv(indio_dev);
+
+	pm_runtime_get_sync(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+	pm_runtime_disable(&client->dev);
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	ak8975_set_mode(data, POWER_DOWN);
+	ak8975_power_off(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak8975_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ak8975_data *data = iio_priv(indio_dev);
+	int ret;
+
+	/* Set the device in power down if it wasn't already */
+	ret = ak8975_set_mode(data, POWER_DOWN);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting power-down mode\n");
+		return ret;
+	}
+	/* Next cut the regulators */
+	ak8975_power_off(data);
+
+	return 0;
+}
+
+static int ak8975_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ak8975_data *data = iio_priv(indio_dev);
+	int ret;
+
+	/* Take up the regulators */
+	ak8975_power_on(data);
+	/*
+	 * We come up in powered down mode, the reading routines will
+	 * put us in the mode to read values later.
+	 */
+	ret = ak8975_set_mode(data, POWER_DOWN);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting power-down mode\n");
+		return ret;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ak8975_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(ak8975_runtime_suspend,
+			   ak8975_runtime_resume, NULL)
+};
+
+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",
+		.pm = &ak8975_dev_pm_ops,
+		.of_match_table = of_match_ptr(ak8975_of_match),
+		.acpi_match_table = ACPI_PTR(ak_acpi_match),
+	},
+	.probe		= ak8975_probe,
+	.remove		= ak8975_remove,
+	.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/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.c
new file mode 100644
index 0000000..d104fb8
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.c
@@ -0,0 +1,1053 @@
+/*
+ * 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/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>
+
+#include "bmc150_magn.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 device *dev;
+	/*
+	 * 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;
+	int irq;
+};
+
+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;
+	}
+}
+
+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,
+};
+EXPORT_SYMBOL(bmc150_magn_regmap_config);
+
+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->dev);
+	} else {
+		pm_runtime_mark_last_busy(data->dev);
+		ret = pm_runtime_put_autosuspend(data->dev);
+	}
+
+	if (ret < 0) {
+		dev_err(data->dev,
+			"failed to change power state to %d\n", on);
+		if (on)
+			pm_runtime_put_noidle(data->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->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->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->dev, "Failed reading chip id\n");
+		goto err_poweroff;
+	}
+	if (chip_id != BMC150_MAGN_CHIP_ID_VAL) {
+		dev_err(data->dev, "Invalid chip id 0x%x\n", chip_id);
+		ret = -ENODEV;
+		goto err_poweroff;
+	}
+	dev_dbg(data->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->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->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->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->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);
+}
+
+int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
+		      int irq, const char *name)
+{
+	struct bmc150_magn_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	dev_set_drvdata(dev, indio_dev);
+	data->regmap = regmap;
+	data->irq = irq;
+	data->dev = dev;
+
+	if (!name && ACPI_HANDLE(dev))
+		name = bmc150_magn_match_acpi_device(dev);
+
+	mutex_init(&data->mutex);
+
+	ret = bmc150_magn_init(data);
+	if (ret < 0)
+		return ret;
+
+	indio_dev->dev.parent = 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 (irq > 0) {
+		data->dready_trig = devm_iio_trigger_alloc(dev,
+							   "%s-dev%d",
+							   indio_dev->name,
+							   indio_dev->id);
+		if (!data->dready_trig) {
+			ret = -ENOMEM;
+			dev_err(dev, "iio trigger alloc failed\n");
+			goto err_poweroff;
+		}
+
+		data->dready_trig->dev.parent = 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(dev, "iio trigger register failed\n");
+			goto err_poweroff;
+		}
+
+		ret = request_threaded_irq(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(dev, "request irq %d failed\n", 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(dev, "iio triggered buffer setup failed\n");
+		goto err_free_irq;
+	}
+
+	ret = pm_runtime_set_active(dev);
+	if (ret)
+		goto err_buffer_cleanup;
+
+	pm_runtime_enable(dev);
+	pm_runtime_set_autosuspend_delay(dev,
+					 BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "unable to register iio device\n");
+		goto err_buffer_cleanup;
+	}
+
+	dev_dbg(dev, "Registered device %s\n", name);
+	return 0;
+
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_free_irq:
+	if (irq > 0)
+		free_irq(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;
+}
+EXPORT_SYMBOL(bmc150_magn_probe);
+
+int bmc150_magn_remove(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_put_noidle(dev);
+
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	if (data->irq > 0)
+		free_irq(data->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;
+}
+EXPORT_SYMBOL(bmc150_magn_remove);
+
+#ifdef CONFIG_PM
+static int bmc150_magn_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(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(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 = dev_get_drvdata(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 = dev_get_drvdata(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 = dev_get_drvdata(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
+
+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)
+};
+EXPORT_SYMBOL(bmc150_magn_pm_ops);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 magnetometer core driver");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.h b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.h
new file mode 100644
index 0000000..3b69232
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BMC150_MAGN_H_
+#define _BMC150_MAGN_H_
+
+extern const struct regmap_config bmc150_magn_regmap_config;
+extern const struct dev_pm_ops bmc150_magn_pm_ops;
+
+int bmc150_magn_probe(struct device *dev, struct regmap *regmap, int irq,
+		      const char *name);
+int bmc150_magn_remove(struct device *dev);
+
+#endif /* _BMC150_MAGN_H_ */
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_i2c.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_i2c.c
new file mode 100644
index 0000000..57e40dd
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_i2c.c
@@ -0,0 +1,89 @@
+/*
+ * 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips:
+ *  - BMC150
+ *  - BMC156
+ *  - BMM150
+ *
+ * Copyright (c) 2016, 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.
+ */
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+
+#include "bmc150_magn.h"
+
+static int bmc150_magn_i2c_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	const char *name = NULL;
+
+	regmap = devm_regmap_init_i2c(client, &bmc150_magn_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Failed to initialize i2c regmap\n");
+		return PTR_ERR(regmap);
+	}
+
+	if (id)
+		name = id->name;
+
+	return bmc150_magn_probe(&client->dev, regmap, client->irq, name);
+}
+
+static int bmc150_magn_i2c_remove(struct i2c_client *client)
+{
+	return bmc150_magn_remove(&client->dev);
+}
+
+static const struct acpi_device_id bmc150_magn_acpi_match[] = {
+	{"BMC150B", 0},
+	{"BMC156B", 0},
+	{"BMM150B", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
+
+static const struct i2c_device_id bmc150_magn_i2c_id[] = {
+	{"bmc150_magn",	0},
+	{"bmc156_magn", 0},
+	{"bmm150_magn", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id);
+
+static const struct of_device_id bmc150_magn_of_match[] = {
+	{ .compatible = "bosch,bmc150_magn" },
+	{ .compatible = "bosch,bmc156_magn" },
+	{ .compatible = "bosch,bmm150_magn" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bmc150_magn_of_match);
+
+static struct i2c_driver bmc150_magn_driver = {
+	.driver = {
+		.name	= "bmc150_magn_i2c",
+		.of_match_table = bmc150_magn_of_match,
+		.acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
+		.pm	= &bmc150_magn_pm_ops,
+	},
+	.probe		= bmc150_magn_i2c_probe,
+	.remove		= bmc150_magn_i2c_remove,
+	.id_table	= bmc150_magn_i2c_id,
+};
+module_i2c_driver(bmc150_magn_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 I2C magnetometer driver");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_spi.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_spi.c
new file mode 100644
index 0000000..7d4152d
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/bmc150_magn_spi.c
@@ -0,0 +1,71 @@
+/*
+ * 3-axis magnetometer driver support following SPI Bosch-Sensortec chips:
+ *  - BMC150
+ *  - BMC156
+ *  - BMM150
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ */
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+
+#include "bmc150_magn.h"
+
+static int bmc150_magn_spi_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	const struct spi_device_id *id = spi_get_device_id(spi);
+
+	regmap = devm_regmap_init_spi(spi, &bmc150_magn_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+	return bmc150_magn_probe(&spi->dev, regmap, spi->irq, id->name);
+}
+
+static int bmc150_magn_spi_remove(struct spi_device *spi)
+{
+	bmc150_magn_remove(&spi->dev);
+
+	return 0;
+}
+
+static const struct spi_device_id bmc150_magn_spi_id[] = {
+	{"bmc150_magn", 0},
+	{"bmc156_magn", 0},
+	{"bmm150_magn", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id);
+
+static const struct acpi_device_id bmc150_magn_acpi_match[] = {
+	{"BMC150B", 0},
+	{"BMC156B", 0},
+	{"BMM150B", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
+
+static struct spi_driver bmc150_magn_spi_driver = {
+	.probe		= bmc150_magn_spi_probe,
+	.remove		= bmc150_magn_spi_remove,
+	.id_table	= bmc150_magn_spi_id,
+	.driver = {
+		.acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
+		.name	= "bmc150_magn_spi",
+	},
+};
+module_spi_driver(bmc150_magn_spi_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_DESCRIPTION("BMC150 magnetometer SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hid-sensor-magn-3d.c
new file mode 100644
index 0000000..b495107
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -0,0 +1,614 @@
+/*
+ * 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 common_attributes {
+	int scale_pre_decml;
+	int scale_post_decml;
+	int scale_precision;
+	int value_offset;
+};
+
+struct magn_3d_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_common magn_flux_attributes;
+	struct hid_sensor_common rot_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];
+
+	struct common_attributes magn_flux_attr;
+	struct common_attributes rot_attr;
+};
+
+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;
+	s32 min;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
+		report_id = magn_state->magn[chan->address].report_id;
+		min = magn_state->magn[chan->address].logical_minimum;
+		address = magn_3d_addresses[chan->address];
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				magn_state->magn_flux_attributes.hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D, address,
+				report_id,
+				SENSOR_HUB_SYNC,
+				min < 0);
+		else {
+			*val = 0;
+			hid_sensor_power_state(
+				&magn_state->magn_flux_attributes,
+				false);
+			return -EINVAL;
+		}
+		hid_sensor_power_state(&magn_state->magn_flux_attributes,
+					false);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*val = magn_state->magn_flux_attr.scale_pre_decml;
+			*val2 = magn_state->magn_flux_attr.scale_post_decml;
+			ret_type = magn_state->magn_flux_attr.scale_precision;
+			break;
+		case IIO_ROT:
+			*val = magn_state->rot_attr.scale_pre_decml;
+			*val2 = magn_state->rot_attr.scale_post_decml;
+			ret_type = magn_state->rot_attr.scale_precision;
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*val = magn_state->magn_flux_attr.value_offset;
+			ret_type = IIO_VAL_INT;
+			break;
+		case IIO_ROT:
+			*val = magn_state->rot_attr.value_offset;
+			ret_type = IIO_VAL_INT;
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret_type = hid_sensor_read_samp_freq_value(
+			&magn_state->magn_flux_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		switch (chan->type) {
+		case IIO_MAGN:
+			ret_type = hid_sensor_read_raw_hyst_value(
+				&magn_state->magn_flux_attributes, val, val2);
+			break;
+		case IIO_ROT:
+			ret_type = hid_sensor_read_raw_hyst_value(
+				&magn_state->rot_attributes, val, val2);
+			break;
+		default:
+			ret_type = -EINVAL;
+		}
+		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->magn_flux_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		switch (chan->type) {
+		case IIO_MAGN:
+			ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->magn_flux_attributes, val, val2);
+			break;
+		case IIO_ROT:
+			ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->rot_attributes, val, val2);
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		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->magn_flux_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->magn_flux_attr.scale_precision = hid_sensor_format_scale(
+				HID_USAGE_SENSOR_COMPASS_3D,
+				&st->magn[CHANNEL_SCAN_INDEX_X],
+				&st->magn_flux_attr.scale_pre_decml,
+				&st->magn_flux_attr.scale_post_decml);
+	st->rot_attr.scale_precision
+		= hid_sensor_format_scale(
+			HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
+			&st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],
+			&st->rot_attr.scale_pre_decml,
+			&st->rot_attr.scale_post_decml);
+
+	/* Set Sensitivity field ids, when there is no individual modifier */
+	if (st->magn_flux_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->magn_flux_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->magn_flux_attributes.sensitivity.index,
+			st->magn_flux_attributes.sensitivity.report_id);
+	}
+	if (st->magn_flux_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_ORIENT_MAGN_FLUX,
+			&st->magn_flux_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->magn_flux_attributes.sensitivity.index,
+			st->magn_flux_attributes.sensitivity.report_id);
+	}
+	if (st->rot_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_ORIENT_COMP_MAGN_NORTH,
+			&st->rot_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->rot_attributes.sensitivity.index,
+			st->rot_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->magn_flux_attributes.hsdev = hsdev;
+	magn_state->magn_flux_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D,
+				&magn_state->magn_flux_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		return ret;
+	}
+	magn_state->rot_attributes = magn_state->magn_flux_attributes;
+
+	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->magn_flux_attributes.data_ready, 0);
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+					&magn_state->magn_flux_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->magn_flux_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->magn_flux_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/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843.h b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843.h
new file mode 100644
index 0000000..76a5d74
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843.h
@@ -0,0 +1,65 @@
+/*
+ * Header file for hmc5843 driver
+ *
+ * Split from hmc5843.c
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef HMC5843_CORE_H
+#define HMC5843_CORE_H
+
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+
+#define HMC5843_CONFIG_REG_A			0x00
+#define HMC5843_CONFIG_REG_B			0x01
+#define HMC5843_MODE_REG			0x02
+#define HMC5843_DATA_OUT_MSB_REGS		0x03
+#define HMC5843_STATUS_REG			0x09
+#define HMC5843_ID_REG				0x0a
+#define HMC5843_ID_END				0x0c
+
+enum hmc5843_ids {
+	HMC5843_ID,
+	HMC5883_ID,
+	HMC5883L_ID,
+	HMC5983_ID,
+};
+
+/**
+ * struct hcm5843_data	- device specific data
+ * @dev:		actual device
+ * @lock:		update and read regmap data
+ * @regmap:		hardware access register maps
+ * @variant:		describe chip variants
+ * @buffer:		3x 16-bit channels + padding + 64-bit timestamp
+ */
+struct hmc5843_data {
+	struct device *dev;
+	struct mutex lock;
+	struct regmap *regmap;
+	const struct hmc5843_chip_info *variant;
+	__be16 buffer[8];
+};
+
+int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
+			 enum hmc5843_ids id, const char *name);
+int hmc5843_common_remove(struct device *dev);
+
+int hmc5843_common_suspend(struct device *dev);
+int hmc5843_common_resume(struct device *dev);
+
+#ifdef CONFIG_PM_SLEEP
+static SIMPLE_DEV_PM_OPS(hmc5843_pm_ops,
+		hmc5843_common_suspend,
+		hmc5843_common_resume);
+#define HMC5843_PM_OPS (&hmc5843_pm_ops)
+#else
+#define HMC5843_PM_OPS NULL
+#endif
+
+#endif /* HMC5843_CORE_H */
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_core.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_core.c
new file mode 100644
index 0000000..ba3e2a3
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_core.c
@@ -0,0 +1,686 @@
+/*
+ * Device driver for the the HMC5843 multi-chip module designed
+ * for low field magnetic sensing.
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
+ * Acknowledgment: Jonathan Cameron <jic23@kernel.org> for valuable inputs.
+ * Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
+ * Split to multiple files by Josef Gajdusek <atx@atx.name> - 2014
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.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>
+
+#include "hmc5843.h"
+
+/*
+ * Range gain settings in (+-)Ga
+ * Beware: HMC5843 and HMC5883 have different recommended sensor field
+ * ranges; default corresponds to +-1.0 Ga and +-1.3 Ga, respectively
+ */
+#define HMC5843_RANGE_GAIN_OFFSET		0x05
+#define HMC5843_RANGE_GAIN_DEFAULT		0x01
+#define HMC5843_RANGE_GAIN_MASK		0xe0
+
+/* Device status */
+#define HMC5843_DATA_READY			0x01
+#define HMC5843_DATA_OUTPUT_LOCK		0x02
+
+/* Mode register configuration */
+#define HMC5843_MODE_CONVERSION_CONTINUOUS	0x00
+#define HMC5843_MODE_CONVERSION_SINGLE		0x01
+#define HMC5843_MODE_IDLE			0x02
+#define HMC5843_MODE_SLEEP			0x03
+#define HMC5843_MODE_MASK			0x03
+
+/*
+ * HMC5843: Minimum data output rate
+ * HMC5883: Typical data output rate
+ */
+#define HMC5843_RATE_OFFSET			0x02
+#define HMC5843_RATE_DEFAULT			0x04
+#define HMC5843_RATE_MASK		0x1c
+
+/* Device measurement configuration */
+#define HMC5843_MEAS_CONF_NORMAL		0x00
+#define HMC5843_MEAS_CONF_POSITIVE_BIAS		0x01
+#define HMC5843_MEAS_CONF_NEGATIVE_BIAS		0x02
+#define HMC5843_MEAS_CONF_MASK			0x03
+
+/*
+ * API for setting the measurement configuration to
+ * Normal, Positive bias and Negative bias
+ *
+ * From the datasheet:
+ * 0 - Normal measurement configuration (default): In normal measurement
+ *     configuration the device follows normal measurement flow. Pins BP
+ *     and BN are left floating and high impedance.
+ *
+ * 1 - Positive bias configuration: In positive bias configuration, a
+ *     positive current is forced across the resistive load on pins BP
+ *     and BN.
+ *
+ * 2 - Negative bias configuration. In negative bias configuration, a
+ *     negative current is forced across the resistive load on pins BP
+ *     and BN.
+ *
+ * 3 - Only available on HMC5983. Magnetic sensor is disabled.
+ *     Temperature sensor is enabled.
+ */
+
+static const char *const hmc5843_meas_conf_modes[] = {"normal", "positivebias",
+						      "negativebias"};
+
+static const char *const hmc5983_meas_conf_modes[] = {"normal", "positivebias",
+						      "negativebias",
+						      "disabled"};
+/* Scaling factors: 10000000/Gain */
+static const int hmc5843_regval_to_nanoscale[] = {
+	6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
+};
+
+static const int hmc5883_regval_to_nanoscale[] = {
+	7812, 9766, 13021, 16287, 24096, 27701, 32573, 45662
+};
+
+static const int hmc5883l_regval_to_nanoscale[] = {
+	7299, 9174, 12195, 15152, 22727, 25641, 30303, 43478
+};
+
+/*
+ * From the datasheet:
+ * Value	| HMC5843		| HMC5883/HMC5883L
+ *		| Data output rate (Hz)	| Data output rate (Hz)
+ * 0		| 0.5			| 0.75
+ * 1		| 1			| 1.5
+ * 2		| 2			| 3
+ * 3		| 5			| 7.5
+ * 4		| 10 (default)		| 15
+ * 5		| 20			| 30
+ * 6		| 50			| 75
+ * 7		| Not used		| Not used
+ */
+static const int hmc5843_regval_to_samp_freq[][2] = {
+	{0, 500000}, {1, 0}, {2, 0}, {5, 0}, {10, 0}, {20, 0}, {50, 0}
+};
+
+static const int hmc5883_regval_to_samp_freq[][2] = {
+	{0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
+	{75, 0}
+};
+
+static const int hmc5983_regval_to_samp_freq[][2] = {
+	{0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
+	{75, 0}, {220, 0}
+};
+
+/* Describe chip variants */
+struct hmc5843_chip_info {
+	const struct iio_chan_spec *channels;
+	const int (*regval_to_samp_freq)[2];
+	const int n_regval_to_samp_freq;
+	const int *regval_to_nanoscale;
+	const int n_regval_to_nanoscale;
+};
+
+/* The lower two bits contain the current conversion mode */
+static s32 hmc5843_set_mode(struct hmc5843_data *data, u8 operating_mode)
+{
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = regmap_update_bits(data->regmap, HMC5843_MODE_REG,
+				 HMC5843_MODE_MASK, operating_mode);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int hmc5843_wait_measurement(struct hmc5843_data *data)
+{
+	int tries = 150;
+	unsigned int val;
+	int ret;
+
+	while (tries-- > 0) {
+		ret = regmap_read(data->regmap, HMC5843_STATUS_REG, &val);
+		if (ret < 0)
+			return ret;
+		if (val & HMC5843_DATA_READY)
+			break;
+		msleep(20);
+	}
+
+	if (tries < 0) {
+		dev_err(data->dev, "data not ready\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* Return the measurement value from the specified channel */
+static int hmc5843_read_measurement(struct hmc5843_data *data,
+				    int idx, int *val)
+{
+	__be16 values[3];
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = hmc5843_wait_measurement(data);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+	ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
+			       values, sizeof(values));
+	mutex_unlock(&data->lock);
+	if (ret < 0)
+		return ret;
+
+	*val = sign_extend32(be16_to_cpu(values[idx]), 15);
+	return IIO_VAL_INT;
+}
+
+static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)
+{
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
+				 HMC5843_MEAS_CONF_MASK, meas_conf);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static
+int hmc5843_show_measurement_configuration(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan)
+{
+	struct hmc5843_data *data = iio_priv(indio_dev);
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &val);
+	if (ret)
+		return ret;
+
+	return val & HMC5843_MEAS_CONF_MASK;
+}
+
+static
+int hmc5843_set_measurement_configuration(struct iio_dev *indio_dev,
+					  const struct iio_chan_spec *chan,
+					  unsigned int meas_conf)
+{
+	struct hmc5843_data *data = iio_priv(indio_dev);
+
+	return hmc5843_set_meas_conf(data, meas_conf);
+}
+
+static const struct iio_enum hmc5843_meas_conf_enum = {
+	.items = hmc5843_meas_conf_modes,
+	.num_items = ARRAY_SIZE(hmc5843_meas_conf_modes),
+	.get = hmc5843_show_measurement_configuration,
+	.set = hmc5843_set_measurement_configuration,
+};
+
+static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = {
+	IIO_ENUM("meas_conf", true, &hmc5843_meas_conf_enum),
+	IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum),
+	{ },
+};
+
+static const struct iio_enum hmc5983_meas_conf_enum = {
+	.items = hmc5983_meas_conf_modes,
+	.num_items = ARRAY_SIZE(hmc5983_meas_conf_modes),
+	.get = hmc5843_show_measurement_configuration,
+	.set = hmc5843_set_measurement_configuration,
+};
+
+static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = {
+	IIO_ENUM("meas_conf", true, &hmc5983_meas_conf_enum),
+	IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum),
+	{ },
+};
+
+static
+ssize_t hmc5843_show_samp_freq_avail(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+	size_t len = 0;
+	int i;
+
+	for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+			"%d.%d ", data->variant->regval_to_samp_freq[i][0],
+			data->variant->regval_to_samp_freq[i][1]);
+
+	/* replace trailing space by newline */
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hmc5843_show_samp_freq_avail);
+
+static int hmc5843_set_samp_freq(struct hmc5843_data *data, u8 rate)
+{
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
+				 HMC5843_RATE_MASK,
+				 rate << HMC5843_RATE_OFFSET);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int hmc5843_get_samp_freq_index(struct hmc5843_data *data,
+				       int val, int val2)
+{
+	int i;
+
+	for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
+		if (val == data->variant->regval_to_samp_freq[i][0] &&
+		    val2 == data->variant->regval_to_samp_freq[i][1])
+			return i;
+
+	return -EINVAL;
+}
+
+static int hmc5843_set_range_gain(struct hmc5843_data *data, u8 range)
+{
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_B,
+				 HMC5843_RANGE_GAIN_MASK,
+				 range << HMC5843_RANGE_GAIN_OFFSET);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static ssize_t hmc5843_show_scale_avail(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+
+	size_t len = 0;
+	int i;
+
+	for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+			"0.%09d ", data->variant->regval_to_nanoscale[i]);
+
+	/* replace trailing space by newline */
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(scale_available, S_IRUGO,
+	hmc5843_show_scale_avail, NULL, 0);
+
+static int hmc5843_get_scale_index(struct hmc5843_data *data, int val, int val2)
+{
+	int i;
+
+	if (val)
+		return -EINVAL;
+
+	for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
+		if (val2 == data->variant->regval_to_nanoscale[i])
+			return i;
+
+	return -EINVAL;
+}
+
+static int hmc5843_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct hmc5843_data *data = iio_priv(indio_dev);
+	unsigned int rval;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return hmc5843_read_measurement(data, chan->scan_index, val);
+	case IIO_CHAN_INFO_SCALE:
+		ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_B, &rval);
+		if (ret < 0)
+			return ret;
+		rval >>= HMC5843_RANGE_GAIN_OFFSET;
+		*val = 0;
+		*val2 = data->variant->regval_to_nanoscale[rval];
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &rval);
+		if (ret < 0)
+			return ret;
+		rval >>= HMC5843_RATE_OFFSET;
+		*val = data->variant->regval_to_samp_freq[rval][0];
+		*val2 = data->variant->regval_to_samp_freq[rval][1];
+		return IIO_VAL_INT_PLUS_MICRO;
+	}
+	return -EINVAL;
+}
+
+static int hmc5843_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct hmc5843_data *data = iio_priv(indio_dev);
+	int rate, range;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		rate = hmc5843_get_samp_freq_index(data, val, val2);
+		if (rate < 0)
+			return -EINVAL;
+
+		return hmc5843_set_samp_freq(data, rate);
+	case IIO_CHAN_INFO_SCALE:
+		range = hmc5843_get_scale_index(data, val, val2);
+		if (range < 0)
+			return -EINVAL;
+
+		return hmc5843_set_range_gain(data, range);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int hmc5843_write_raw_get_fmt(struct iio_dev *indio_dev,
+				     struct iio_chan_spec const *chan,
+				     long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SCALE:
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t hmc5843_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct hmc5843_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = hmc5843_wait_measurement(data);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		goto done;
+	}
+
+	ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
+			       data->buffer, 3 * sizeof(__be16));
+
+	mutex_unlock(&data->lock);
+	if (ret < 0)
+		goto done;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+					   iio_get_time_ns(indio_dev));
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+#define HMC5843_CHANNEL(axis, idx)					\
+	{								\
+		.type = IIO_MAGN,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
+			BIT(IIO_CHAN_INFO_SAMP_FREQ),			\
+		.scan_index = idx,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_BE,				\
+		},							\
+		.ext_info = hmc5843_ext_info,	\
+	}
+
+#define HMC5983_CHANNEL(axis, idx)					\
+	{								\
+		.type = IIO_MAGN,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
+			BIT(IIO_CHAN_INFO_SAMP_FREQ),			\
+		.scan_index = idx,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_BE,				\
+		},							\
+		.ext_info = hmc5983_ext_info,	\
+	}
+
+static const struct iio_chan_spec hmc5843_channels[] = {
+	HMC5843_CHANNEL(X, 0),
+	HMC5843_CHANNEL(Y, 1),
+	HMC5843_CHANNEL(Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+/* Beware: Y and Z are exchanged on HMC5883 and 5983 */
+static const struct iio_chan_spec hmc5883_channels[] = {
+	HMC5843_CHANNEL(X, 0),
+	HMC5843_CHANNEL(Z, 1),
+	HMC5843_CHANNEL(Y, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec hmc5983_channels[] = {
+	HMC5983_CHANNEL(X, 0),
+	HMC5983_CHANNEL(Z, 1),
+	HMC5983_CHANNEL(Y, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static struct attribute *hmc5843_attributes[] = {
+	&iio_dev_attr_scale_available.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group hmc5843_group = {
+	.attrs = hmc5843_attributes,
+};
+
+static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = {
+	[HMC5843_ID] = {
+		.channels = hmc5843_channels,
+		.regval_to_samp_freq = hmc5843_regval_to_samp_freq,
+		.n_regval_to_samp_freq =
+				ARRAY_SIZE(hmc5843_regval_to_samp_freq),
+		.regval_to_nanoscale = hmc5843_regval_to_nanoscale,
+		.n_regval_to_nanoscale =
+				ARRAY_SIZE(hmc5843_regval_to_nanoscale),
+	},
+	[HMC5883_ID] = {
+		.channels = hmc5883_channels,
+		.regval_to_samp_freq = hmc5883_regval_to_samp_freq,
+		.n_regval_to_samp_freq =
+				ARRAY_SIZE(hmc5883_regval_to_samp_freq),
+		.regval_to_nanoscale = hmc5883_regval_to_nanoscale,
+		.n_regval_to_nanoscale =
+				ARRAY_SIZE(hmc5883_regval_to_nanoscale),
+	},
+	[HMC5883L_ID] = {
+		.channels = hmc5883_channels,
+		.regval_to_samp_freq = hmc5883_regval_to_samp_freq,
+		.n_regval_to_samp_freq =
+				ARRAY_SIZE(hmc5883_regval_to_samp_freq),
+		.regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
+		.n_regval_to_nanoscale =
+				ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
+	},
+	[HMC5983_ID] = {
+		.channels = hmc5983_channels,
+		.regval_to_samp_freq = hmc5983_regval_to_samp_freq,
+		.n_regval_to_samp_freq =
+				ARRAY_SIZE(hmc5983_regval_to_samp_freq),
+		.regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
+		.n_regval_to_nanoscale =
+				ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
+	}
+};
+
+static int hmc5843_init(struct hmc5843_data *data)
+{
+	int ret;
+	u8 id[3];
+
+	ret = regmap_bulk_read(data->regmap, HMC5843_ID_REG,
+			       id, ARRAY_SIZE(id));
+	if (ret < 0)
+		return ret;
+	if (id[0] != 'H' || id[1] != '4' || id[2] != '3') {
+		dev_err(data->dev, "no HMC5843/5883/5883L/5983 sensor\n");
+		return -ENODEV;
+	}
+
+	ret = hmc5843_set_meas_conf(data, HMC5843_MEAS_CONF_NORMAL);
+	if (ret < 0)
+		return ret;
+	ret = hmc5843_set_samp_freq(data, HMC5843_RATE_DEFAULT);
+	if (ret < 0)
+		return ret;
+	ret = hmc5843_set_range_gain(data, HMC5843_RANGE_GAIN_DEFAULT);
+	if (ret < 0)
+		return ret;
+	return hmc5843_set_mode(data, HMC5843_MODE_CONVERSION_CONTINUOUS);
+}
+
+static const struct iio_info hmc5843_info = {
+	.attrs = &hmc5843_group,
+	.read_raw = &hmc5843_read_raw,
+	.write_raw = &hmc5843_write_raw,
+	.write_raw_get_fmt = &hmc5843_write_raw_get_fmt,
+	.driver_module = THIS_MODULE,
+};
+
+static const unsigned long hmc5843_scan_masks[] = {0x7, 0};
+
+int hmc5843_common_suspend(struct device *dev)
+{
+	return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
+				HMC5843_MODE_SLEEP);
+}
+EXPORT_SYMBOL(hmc5843_common_suspend);
+
+int hmc5843_common_resume(struct device *dev)
+{
+	return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
+		HMC5843_MODE_CONVERSION_CONTINUOUS);
+}
+EXPORT_SYMBOL(hmc5843_common_resume);
+
+int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
+			 enum hmc5843_ids id, const char *name)
+{
+	struct hmc5843_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, indio_dev);
+
+	/* default settings at probe */
+	data = iio_priv(indio_dev);
+	data->dev = dev;
+	data->regmap = regmap;
+	data->variant = &hmc5843_chip_info_tbl[id];
+	mutex_init(&data->lock);
+
+	indio_dev->dev.parent = dev;
+	indio_dev->name = name;
+	indio_dev->info = &hmc5843_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = data->variant->channels;
+	indio_dev->num_channels = 4;
+	indio_dev->available_scan_masks = hmc5843_scan_masks;
+
+	ret = hmc5843_init(data);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 hmc5843_trigger_handler, NULL);
+	if (ret < 0)
+		goto buffer_setup_err;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto buffer_cleanup;
+
+	return 0;
+
+buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+buffer_setup_err:
+	hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
+	return ret;
+}
+EXPORT_SYMBOL(hmc5843_common_probe);
+
+int hmc5843_common_remove(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	/*  sleep mode to save power */
+	hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
+
+	return 0;
+}
+EXPORT_SYMBOL(hmc5843_common_remove);
+
+MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>");
+MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 core driver");
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_i2c.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_i2c.c
new file mode 100644
index 0000000..86abba5
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_i2c.c
@@ -0,0 +1,108 @@
+/*
+ * i2c driver for hmc5843/5843/5883/5883l/5983
+ *
+ * Split from hmc5843.c
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "hmc5843.h"
+
+static const struct regmap_range hmc5843_readable_ranges[] = {
+	regmap_reg_range(0, HMC5843_ID_END),
+};
+
+static const struct regmap_access_table hmc5843_readable_table = {
+	.yes_ranges = hmc5843_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
+};
+
+static const struct regmap_range hmc5843_writable_ranges[] = {
+	regmap_reg_range(0, HMC5843_MODE_REG),
+};
+
+static const struct regmap_access_table hmc5843_writable_table = {
+	.yes_ranges = hmc5843_writable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
+};
+
+static const struct regmap_range hmc5843_volatile_ranges[] = {
+	regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
+};
+
+static const struct regmap_access_table hmc5843_volatile_table = {
+	.yes_ranges = hmc5843_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
+};
+
+static const struct regmap_config hmc5843_i2c_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.rd_table = &hmc5843_readable_table,
+	.wr_table = &hmc5843_writable_table,
+	.volatile_table = &hmc5843_volatile_table,
+
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int hmc5843_i2c_probe(struct i2c_client *cli,
+			     const struct i2c_device_id *id)
+{
+	struct regmap *regmap = devm_regmap_init_i2c(cli,
+			&hmc5843_i2c_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return hmc5843_common_probe(&cli->dev,
+			regmap,
+			id->driver_data, id->name);
+}
+
+static int hmc5843_i2c_remove(struct i2c_client *client)
+{
+	return hmc5843_common_remove(&client->dev);
+}
+
+static const struct i2c_device_id hmc5843_id[] = {
+	{ "hmc5843", HMC5843_ID },
+	{ "hmc5883", HMC5883_ID },
+	{ "hmc5883l", HMC5883L_ID },
+	{ "hmc5983", HMC5983_ID },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, hmc5843_id);
+
+static const struct of_device_id hmc5843_of_match[] = {
+	{ .compatible = "honeywell,hmc5843", .data = (void *)HMC5843_ID },
+	{ .compatible = "honeywell,hmc5883", .data = (void *)HMC5883_ID },
+	{ .compatible = "honeywell,hmc5883l", .data = (void *)HMC5883L_ID },
+	{ .compatible = "honeywell,hmc5983", .data = (void *)HMC5983_ID },
+	{}
+};
+MODULE_DEVICE_TABLE(of, hmc5843_of_match);
+
+static struct i2c_driver hmc5843_driver = {
+	.driver = {
+		.name	= "hmc5843",
+		.pm	= HMC5843_PM_OPS,
+		.of_match_table = hmc5843_of_match,
+	},
+	.id_table	= hmc5843_id,
+	.probe		= hmc5843_i2c_probe,
+	.remove		= hmc5843_i2c_remove,
+};
+module_i2c_driver(hmc5843_driver);
+
+MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
+MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 i2c driver");
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_spi.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_spi.c
new file mode 100644
index 0000000..79b2b70
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/hmc5843_spi.c
@@ -0,0 +1,105 @@
+/*
+ * SPI driver for hmc5983
+ *
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include "hmc5843.h"
+
+static const struct regmap_range hmc5843_readable_ranges[] = {
+		regmap_reg_range(0, HMC5843_ID_END),
+};
+
+static const struct regmap_access_table hmc5843_readable_table = {
+		.yes_ranges = hmc5843_readable_ranges,
+		.n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
+};
+
+static const struct regmap_range hmc5843_writable_ranges[] = {
+		regmap_reg_range(0, HMC5843_MODE_REG),
+};
+
+static const struct regmap_access_table hmc5843_writable_table = {
+		.yes_ranges = hmc5843_writable_ranges,
+		.n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
+};
+
+static const struct regmap_range hmc5843_volatile_ranges[] = {
+		regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
+};
+
+static const struct regmap_access_table hmc5843_volatile_table = {
+		.yes_ranges = hmc5843_volatile_ranges,
+		.n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
+};
+
+static const struct regmap_config hmc5843_spi_regmap_config = {
+		.reg_bits = 8,
+		.val_bits = 8,
+
+		.rd_table = &hmc5843_readable_table,
+		.wr_table = &hmc5843_writable_table,
+		.volatile_table = &hmc5843_volatile_table,
+
+		/* Autoincrement address pointer */
+		.read_flag_mask = 0xc0,
+
+		.cache_type = REGCACHE_RBTREE,
+};
+
+static int hmc5843_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	struct regmap *regmap;
+	const struct spi_device_id *id = spi_get_device_id(spi);
+
+	spi->mode = SPI_MODE_3;
+	spi->max_speed_hz = 8000000;
+	spi->bits_per_word = 8;
+	ret = spi_setup(spi);
+	if (ret)
+		return ret;
+
+	regmap = devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return hmc5843_common_probe(&spi->dev,
+			regmap,
+			id->driver_data, id->name);
+}
+
+static int hmc5843_spi_remove(struct spi_device *spi)
+{
+	return hmc5843_common_remove(&spi->dev);
+}
+
+static const struct spi_device_id hmc5843_id[] = {
+	{ "hmc5983", HMC5983_ID },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, hmc5843_id);
+
+static struct spi_driver hmc5843_driver = {
+		.driver = {
+				.name = "hmc5843",
+				.pm = HMC5843_PM_OPS,
+		},
+		.id_table = hmc5843_id,
+		.probe = hmc5843_spi_probe,
+		.remove = hmc5843_spi_remove,
+};
+
+module_spi_driver(hmc5843_driver);
+
+MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
+MODULE_DESCRIPTION("HMC5983 SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.14/drivers/iio/magnetometer/mag3110.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/mag3110.c
new file mode 100644
index 0000000..dad8d57
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/mag3110.c
@@ -0,0 +1,464 @@
+/*
+ * 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:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		switch (chan->type) {
+		case IIO_MAGN: /* in 0.1 uT / LSB */
+			ret = mag3110_read(data, buffer);
+			if (ret < 0)
+				goto release;
+			*val = sign_extend32(
+				be16_to_cpu(buffer[chan->scan_index]), 15);
+			ret = IIO_VAL_INT;
+			break;
+		case IIO_TEMP: /* in 1 C / LSB */
+			mutex_lock(&data->lock);
+			ret = mag3110_request(data);
+			if (ret < 0) {
+				mutex_unlock(&data->lock);
+				goto release;
+			}
+			ret = i2c_smbus_read_byte_data(data->client,
+				MAG3110_DIE_TEMP);
+			mutex_unlock(&data->lock);
+			if (ret < 0)
+				goto release;
+			*val = sign_extend32(ret, 7);
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+release:
+		iio_device_release_direct_mode(indio_dev);
+		return ret;
+
+	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, ret;
+
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret)
+		return ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		rate = mag3110_get_samp_freq_index(data, val, val2);
+		if (rate < 0) {
+			ret = -EINVAL;
+			break;
+		}
+
+		data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
+		data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
+		ret = i2c_smbus_write_byte_data(data->client,
+			MAG3110_CTRL_REG1, data->ctrl_reg1);
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (val < -10000 || val > 10000) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = i2c_smbus_write_word_swapped(data->client,
+			MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	iio_device_release_direct_mode(indio_dev);
+	return ret;
+}
+
+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(indio_dev));
+
+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 const struct of_device_id mag3110_of_match[] = {
+	{ .compatible = "fsl,mag3110" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mag3110_of_match);
+
+static struct i2c_driver mag3110_driver = {
+	.driver = {
+		.name	= "mag3110",
+		.of_match_table = mag3110_of_match,
+		.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/src/kernel/linux/v4.14/drivers/iio/magnetometer/mmc35240.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/mmc35240.c
new file mode 100644
index 0000000..176e14a
--- /dev/null
+++ b/src/kernel/linux/v4.14/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/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn.h b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn.h
new file mode 100644
index 0000000..8fe51ce
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn.h
@@ -0,0 +1,51 @@
+/*
+ * 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"
+#define LIS2MDL_MAGN_DEV_NAME		"lis2mdl"
+
+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/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_buffer.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_buffer.c
new file mode 100644
index 0000000..37ab305
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_buffer.c
@@ -0,0 +1,90 @@
+/*
+ * 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_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 st_sensors_set_enable(indio_dev, true);
+
+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 = st_sensors_set_enable(indio_dev, false);
+	if (err < 0)
+		goto st_magn_buffer_predisable_error;
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+
+st_magn_buffer_predisable_error:
+	kfree(mdata->buffer_data);
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
+	.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, NULL,
+		&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/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_core.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_core.c
new file mode 100644
index 0000000..19031a7
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_core.c
@@ -0,0 +1,546 @@
+/*
+ * 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
+
+/* Special L addresses for Sensor 2 */
+#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
+
+/* Special L addresses for sensor 3 */
+#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 = 0x00,
+			.mask = 0x1c,
+			.odr_avl = {
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 8, .value = 0x03 },
+				{ .hz = 15, .value = 0x04 },
+				{ .hz = 30, .value = 0x05 },
+				{ .hz = 75, .value = 0x06 },
+				/* 220 Hz, 0x07 reportedly exist */
+			},
+		},
+		.pw = {
+			.addr = 0x02,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
+		},
+		.fs = {
+			.addr = 0x01,
+			.mask = 0xe0,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_1300MG,
+					.value = 0x01,
+					.gain = 1100,
+					.gain2 = 980,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_1900MG,
+					.value = 0x02,
+					.gain = 855,
+					.gain2 = 760,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_2500MG,
+					.value = 0x03,
+					.gain = 670,
+					.gain2 = 600,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = 0x04,
+					.gain = 450,
+					.gain2 = 400,
+				},
+				[4] = {
+					.num = ST_MAGN_FS_AVL_4700MG,
+					.value = 0x05,
+					.gain = 400,
+					.gain2 = 355,
+				},
+				[5] = {
+					.num = ST_MAGN_FS_AVL_5600MG,
+					.value = 0x06,
+					.gain = 330,
+					.gain2 = 295,
+				},
+				[6] = {
+					.num = ST_MAGN_FS_AVL_8100MG,
+					.value = 0x07,
+					.gain = 230,
+					.gain2 = 205,
+				},
+			},
+		},
+		.multi_read_bit = false,
+		.bootime = 2,
+	},
+	{
+		.wai = 0x3c,
+		.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 = 0x00,
+			.mask = 0x1c,
+			.odr_avl = {
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 8, .value = 0x03 },
+				{ .hz = 15, .value = 0x04 },
+				{ .hz = 30, .value = 0x05 },
+				{ .hz = 75, .value = 0x06 },
+				{ .hz = 220, .value = 0x07 },
+			},
+		},
+		.pw = {
+			.addr = 0x02,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
+		},
+		.fs = {
+			.addr = 0x01,
+			.mask = 0xe0,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_1300MG,
+					.value = 0x01,
+					.gain = 909,
+					.gain2 = 1020,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_1900MG,
+					.value = 0x02,
+					.gain = 1169,
+					.gain2 = 1315,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_2500MG,
+					.value = 0x03,
+					.gain = 1492,
+					.gain2 = 1666,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = 0x04,
+					.gain = 2222,
+					.gain2 = 2500,
+				},
+				[4] = {
+					.num = ST_MAGN_FS_AVL_4700MG,
+					.value = 0x05,
+					.gain = 2500,
+					.gain2 = 2816,
+				},
+				[5] = {
+					.num = ST_MAGN_FS_AVL_5600MG,
+					.value = 0x06,
+					.gain = 3030,
+					.gain2 = 3389,
+				},
+				[6] = {
+					.num = ST_MAGN_FS_AVL_8100MG,
+					.value = 0x07,
+					.gain = 4347,
+					.gain2 = 4878,
+				},
+			},
+		},
+		.multi_read_bit = false,
+		.bootime = 2,
+	},
+	{
+		.wai = 0x3d,
+		.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 = 0x20,
+			.mask = 0x1c,
+			.odr_avl = {
+				{ .hz = 1, .value = 0x00 },
+				{ .hz = 2, .value = 0x01 },
+				{ .hz = 3, .value = 0x02 },
+				{ .hz = 5, .value = 0x03 },
+				{ .hz = 10, .value = 0x04 },
+				{ .hz = 20, .value = 0x05 },
+				{ .hz = 40, .value = 0x06 },
+				{ .hz = 80, .value = 0x07 },
+			},
+		},
+		.pw = {
+			.addr = 0x22,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
+		},
+		.fs = {
+			.addr = 0x21,
+			.mask = 0x60,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = 0x00,
+					.gain = 146,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_8000MG,
+					.value = 0x01,
+					.gain = 292,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_12000MG,
+					.value = 0x02,
+					.gain = 438,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_16000MG,
+					.value = 0x03,
+					.gain = 584,
+				},
+			},
+		},
+		.drdy_irq = {
+			/* drdy line is routed drdy pin */
+			.stat_drdy = {
+				.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+				.mask = 0x07,
+			},
+		},
+		.multi_read_bit = true,
+		.bootime = 2,
+	},
+	{
+		.wai = 0x40,
+		.wai_addr = 0x4f,
+		.sensors_supported = {
+			[0] = LSM303AGR_MAGN_DEV_NAME,
+			[1] = LIS2MDL_MAGN_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
+		.odr = {
+			.addr = 0x60,
+			.mask = 0x0c,
+			.odr_avl = {
+				{ .hz = 10, .value = 0x00 },
+				{ .hz = 20, .value = 0x01 },
+				{ .hz = 50, .value = 0x02 },
+				{ .hz = 100, .value = 0x03 },
+			},
+		},
+		.pw = {
+			.addr = 0x60,
+			.mask = 0x03,
+			.value_on = 0x00,
+			.value_off = 0x03,
+		},
+		.fs = {
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_15000MG,
+					.gain = 1500,
+				},
+			},
+		},
+		.bdu = {
+			.addr = 0x62,
+			.mask = 0x10,
+		},
+		.drdy_irq = {
+			.addr = 0x62,
+			.mask_int1 = 0x01,
+			.stat_drdy = {
+				.addr = 0x67,
+				.mask = 0x07,
+			},
+		},
+		.multi_read_bit = false,
+		.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,
+	.validate_device = st_sensors_validate_device,
+};
+#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);
+
+	err = st_sensors_power_enable(indio_dev);
+	if (err)
+		return err;
+
+	err = st_sensors_check_device_support(indio_dev,
+					ARRAY_SIZE(st_magn_sensors_settings),
+					st_magn_sensors_settings);
+	if (err < 0)
+		goto st_magn_power_off;
+
+	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)
+		goto st_magn_power_off;
+
+	err = st_magn_allocate_ring(indio_dev);
+	if (err < 0)
+		goto st_magn_power_off;
+
+	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);
+st_magn_power_off:
+	st_sensors_power_disable(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/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_i2c.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_i2c.c
new file mode 100644
index 0000000..feaa28c
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_i2c.c
@@ -0,0 +1,110 @@
+/*
+ * 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,
+	},
+	{
+		.compatible = "st,lis2mdl",
+		.data = LIS2MDL_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_name_probe(&client->dev, st_magn_of_match,
+				 client->name, sizeof(client->name));
+
+	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 },
+	{ LIS2MDL_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/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_spi.c b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_spi.c
new file mode 100644
index 0000000..7b7cd08
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/iio/magnetometer/st_magn_spi.c
@@ -0,0 +1,99 @@
+/*
+ * 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"
+
+#ifdef CONFIG_OF
+/*
+ * For new single-chip sensors use <device_name> as compatible string.
+ * For old single-chip devices keep <device_name>-magn to maintain
+ * compatibility
+ */
+static const struct of_device_id st_magn_of_match[] = {
+	{
+		.compatible = "st,lis3mdl-magn",
+		.data = LIS3MDL_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lsm303agr-magn",
+		.data = LSM303AGR_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lis2mdl",
+		.data = LIS2MDL_MAGN_DEV_NAME,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, st_magn_of_match);
+#else
+#define st_magn_of_match	NULL
+#endif
+
+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_of_name_probe(&spi->dev, st_magn_of_match,
+				 spi->modalias, sizeof(spi->modalias));
+	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[] = {
+	{ LIS3MDL_MAGN_DEV_NAME },
+	{ LSM303AGR_MAGN_DEV_NAME },
+	{ LIS2MDL_MAGN_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_magn_id_table);
+
+static struct spi_driver st_magn_driver = {
+	.driver = {
+		.name = "st-magn-spi",
+		.of_match_table = of_match_ptr(st_magn_of_match),
+	},
+	.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");