summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/iio/magnetometer
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/iio/magnetometer')
-rw-r--r--kernel/drivers/iio/magnetometer/Kconfig33
-rw-r--r--kernel/drivers/iio/magnetometer/Makefile2
-rw-r--r--kernel/drivers/iio/magnetometer/bmc150_magn.c1085
-rw-r--r--kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.c2
-rw-r--r--kernel/drivers/iio/magnetometer/mmc35240.c595
-rw-r--r--kernel/drivers/iio/magnetometer/st_magn.h4
-rw-r--r--kernel/drivers/iio/magnetometer/st_magn_buffer.c7
-rw-r--r--kernel/drivers/iio/magnetometer/st_magn_core.c215
-rw-r--r--kernel/drivers/iio/magnetometer/st_magn_i2c.c11
-rw-r--r--kernel/drivers/iio/magnetometer/st_magn_spi.c2
10 files changed, 1950 insertions, 6 deletions
diff --git a/kernel/drivers/iio/magnetometer/Kconfig b/kernel/drivers/iio/magnetometer/Kconfig
index a5d6de72c..868abada3 100644
--- a/kernel/drivers/iio/magnetometer/Kconfig
+++ b/kernel/drivers/iio/magnetometer/Kconfig
@@ -8,7 +8,7 @@ menu "Magnetometer sensors"
config AK8975
tristate "Asahi Kasei AK 3-Axis Magnetometer"
depends on I2C
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
Say yes here to build support for Asahi Kasei AK8975, AK8963,
AK09911 or AK09912 3-Axis Magnetometer.
@@ -19,11 +19,29 @@ config AK8975
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select AK8975
help
Deprecated: AK09911 is now supported by AK8975 driver.
+config BMC150_MAGN
+ tristate "Bosch BMC150 Magnetometer Driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the BMC150 magnetometer.
+
+ Currently this only supports the device via an i2c interface.
+
+ This is a combo module with both accelerometer and magnetometer.
+ This driver is only implementing magnetometer part, which has
+ its own address and register map.
+
+ To compile this driver as a module, choose M here: the module will be
+ called bmc150_magn.
+
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"
depends on I2C
@@ -47,6 +65,17 @@ config HID_SENSOR_MAGNETOMETER_3D
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
diff --git a/kernel/drivers/iio/magnetometer/Makefile b/kernel/drivers/iio/magnetometer/Makefile
index 0f5d3c985..2c72df458 100644
--- a/kernel/drivers/iio/magnetometer/Makefile
+++ b/kernel/drivers/iio/magnetometer/Makefile
@@ -4,8 +4,10 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AK8975) += ak8975.o
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
+obj-$(CONFIG_MMC35240) += mmc35240.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
diff --git a/kernel/drivers/iio/magnetometer/bmc150_magn.c b/kernel/drivers/iio/magnetometer/bmc150_magn.c
new file mode 100644
index 000000000..1615b23d7
--- /dev/null
+++ b/kernel/drivers/iio/magnetometer/bmc150_magn.c
@@ -0,0 +1,1085 @@
+/*
+ * Bosch BMC150 three-axis magnetic field sensor driver
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This code is based on bmm050_api.c authored by contact@bosch.sensortec.com:
+ *
+ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+
+#define BMC150_MAGN_DRV_NAME "bmc150_magn"
+#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event"
+
+#define BMC150_MAGN_REG_CHIP_ID 0x40
+#define BMC150_MAGN_CHIP_ID_VAL 0x32
+
+#define BMC150_MAGN_REG_X_L 0x42
+#define BMC150_MAGN_REG_X_M 0x43
+#define BMC150_MAGN_REG_Y_L 0x44
+#define BMC150_MAGN_REG_Y_M 0x45
+#define BMC150_MAGN_SHIFT_XY_L 3
+#define BMC150_MAGN_REG_Z_L 0x46
+#define BMC150_MAGN_REG_Z_M 0x47
+#define BMC150_MAGN_SHIFT_Z_L 1
+#define BMC150_MAGN_REG_RHALL_L 0x48
+#define BMC150_MAGN_REG_RHALL_M 0x49
+#define BMC150_MAGN_SHIFT_RHALL_L 2
+
+#define BMC150_MAGN_REG_INT_STATUS 0x4A
+
+#define BMC150_MAGN_REG_POWER 0x4B
+#define BMC150_MAGN_MASK_POWER_CTL BIT(0)
+
+#define BMC150_MAGN_REG_OPMODE_ODR 0x4C
+#define BMC150_MAGN_MASK_OPMODE GENMASK(2, 1)
+#define BMC150_MAGN_SHIFT_OPMODE 1
+#define BMC150_MAGN_MODE_NORMAL 0x00
+#define BMC150_MAGN_MODE_FORCED 0x01
+#define BMC150_MAGN_MODE_SLEEP 0x03
+#define BMC150_MAGN_MASK_ODR GENMASK(5, 3)
+#define BMC150_MAGN_SHIFT_ODR 3
+
+#define BMC150_MAGN_REG_INT 0x4D
+
+#define BMC150_MAGN_REG_INT_DRDY 0x4E
+#define BMC150_MAGN_MASK_DRDY_EN BIT(7)
+#define BMC150_MAGN_SHIFT_DRDY_EN 7
+#define BMC150_MAGN_MASK_DRDY_INT3 BIT(6)
+#define BMC150_MAGN_MASK_DRDY_Z_EN BIT(5)
+#define BMC150_MAGN_MASK_DRDY_Y_EN BIT(4)
+#define BMC150_MAGN_MASK_DRDY_X_EN BIT(3)
+#define BMC150_MAGN_MASK_DRDY_DR_POLARITY BIT(2)
+#define BMC150_MAGN_MASK_DRDY_LATCHING BIT(1)
+#define BMC150_MAGN_MASK_DRDY_INT3_POLARITY BIT(0)
+
+#define BMC150_MAGN_REG_LOW_THRESH 0x4F
+#define BMC150_MAGN_REG_HIGH_THRESH 0x50
+#define BMC150_MAGN_REG_REP_XY 0x51
+#define BMC150_MAGN_REG_REP_Z 0x52
+#define BMC150_MAGN_REG_REP_DATAMASK GENMASK(7, 0)
+
+#define BMC150_MAGN_REG_TRIM_START 0x5D
+#define BMC150_MAGN_REG_TRIM_END 0x71
+
+#define BMC150_MAGN_XY_OVERFLOW_VAL -4096
+#define BMC150_MAGN_Z_OVERFLOW_VAL -16384
+
+/* Time from SUSPEND to SLEEP */
+#define BMC150_MAGN_START_UP_TIME_MS 3
+
+#define BMC150_MAGN_AUTO_SUSPEND_DELAY_MS 2000
+
+#define BMC150_MAGN_REGVAL_TO_REPXY(regval) (((regval) * 2) + 1)
+#define BMC150_MAGN_REGVAL_TO_REPZ(regval) ((regval) + 1)
+#define BMC150_MAGN_REPXY_TO_REGVAL(rep) (((rep) - 1) / 2)
+#define BMC150_MAGN_REPZ_TO_REGVAL(rep) ((rep) - 1)
+
+enum bmc150_magn_axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+ RHALL,
+ AXIS_XYZ_MAX = RHALL,
+ AXIS_XYZR_MAX,
+};
+
+enum bmc150_magn_power_modes {
+ BMC150_MAGN_POWER_MODE_SUSPEND,
+ BMC150_MAGN_POWER_MODE_SLEEP,
+ BMC150_MAGN_POWER_MODE_NORMAL,
+};
+
+struct bmc150_magn_trim_regs {
+ s8 x1;
+ s8 y1;
+ __le16 reserved1;
+ u8 reserved2;
+ __le16 z4;
+ s8 x2;
+ s8 y2;
+ __le16 reserved3;
+ __le16 z2;
+ __le16 z1;
+ __le16 xyz1;
+ __le16 z3;
+ s8 xy2;
+ u8 xy1;
+} __packed;
+
+struct bmc150_magn_data {
+ struct i2c_client *client;
+ /*
+ * 1. Protect this structure.
+ * 2. Serialize sequences that power on/off the device and access HW.
+ */
+ struct mutex mutex;
+ struct regmap *regmap;
+ /* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
+ s32 buffer[6];
+ struct iio_trigger *dready_trig;
+ bool dready_trigger_on;
+ int max_odr;
+};
+
+static const struct {
+ int freq;
+ u8 reg_val;
+} bmc150_magn_samp_freq_table[] = { {2, 0x01},
+ {6, 0x02},
+ {8, 0x03},
+ {10, 0x00},
+ {15, 0x04},
+ {20, 0x05},
+ {25, 0x06},
+ {30, 0x07} };
+
+enum bmc150_magn_presets {
+ LOW_POWER_PRESET,
+ REGULAR_PRESET,
+ ENHANCED_REGULAR_PRESET,
+ HIGH_ACCURACY_PRESET
+};
+
+static const struct bmc150_magn_preset {
+ u8 rep_xy;
+ u8 rep_z;
+ u8 odr;
+} bmc150_magn_presets_table[] = {
+ [LOW_POWER_PRESET] = {3, 3, 10},
+ [REGULAR_PRESET] = {9, 15, 10},
+ [ENHANCED_REGULAR_PRESET] = {15, 27, 10},
+ [HIGH_ACCURACY_PRESET] = {47, 83, 20},
+};
+
+#define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET
+
+static bool bmc150_magn_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMC150_MAGN_REG_POWER:
+ case BMC150_MAGN_REG_OPMODE_ODR:
+ case BMC150_MAGN_REG_INT:
+ case BMC150_MAGN_REG_INT_DRDY:
+ case BMC150_MAGN_REG_LOW_THRESH:
+ case BMC150_MAGN_REG_HIGH_THRESH:
+ case BMC150_MAGN_REG_REP_XY:
+ case BMC150_MAGN_REG_REP_Z:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool bmc150_magn_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMC150_MAGN_REG_X_L:
+ case BMC150_MAGN_REG_X_M:
+ case BMC150_MAGN_REG_Y_L:
+ case BMC150_MAGN_REG_Y_M:
+ case BMC150_MAGN_REG_Z_L:
+ case BMC150_MAGN_REG_Z_M:
+ case BMC150_MAGN_REG_RHALL_L:
+ case BMC150_MAGN_REG_RHALL_M:
+ case BMC150_MAGN_REG_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bmc150_magn_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMC150_MAGN_REG_TRIM_END,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmc150_magn_is_writeable_reg,
+ .volatile_reg = bmc150_magn_is_volatile_reg,
+};
+
+static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data,
+ enum bmc150_magn_power_modes mode,
+ bool state)
+{
+ int ret;
+
+ switch (mode) {
+ case BMC150_MAGN_POWER_MODE_SUSPEND:
+ ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_POWER,
+ BMC150_MAGN_MASK_POWER_CTL, !state);
+ if (ret < 0)
+ return ret;
+ usleep_range(BMC150_MAGN_START_UP_TIME_MS * 1000, 20000);
+ return 0;
+ case BMC150_MAGN_POWER_MODE_SLEEP:
+ return regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_SLEEP <<
+ BMC150_MAGN_SHIFT_OPMODE);
+ case BMC150_MAGN_POWER_MODE_NORMAL:
+ return regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_NORMAL <<
+ BMC150_MAGN_SHIFT_OPMODE);
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on)
+{
+#ifdef CONFIG_PM
+ int ret;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&data->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&data->client->dev);
+ ret = pm_runtime_put_autosuspend(&data->client->dev);
+ }
+
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to change power state to %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
+ return ret;
+ }
+#endif
+
+ return 0;
+}
+
+static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val)
+{
+ int ret, reg_val;
+ u8 i, odr_val;
+
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, &reg_val);
+ if (ret < 0)
+ return ret;
+ odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++)
+ if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) {
+ *val = bmc150_magn_samp_freq_table[i].freq;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+ if (bmc150_magn_samp_freq_table[i].freq == val) {
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_ODR,
+ bmc150_magn_samp_freq_table[i].
+ reg_val <<
+ BMC150_MAGN_SHIFT_ODR);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_max_odr(struct bmc150_magn_data *data, int rep_xy,
+ int rep_z, int odr)
+{
+ int ret, reg_val, max_odr;
+
+ if (rep_xy <= 0) {
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+ &reg_val);
+ if (ret < 0)
+ return ret;
+ rep_xy = BMC150_MAGN_REGVAL_TO_REPXY(reg_val);
+ }
+ if (rep_z <= 0) {
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+ &reg_val);
+ if (ret < 0)
+ return ret;
+ rep_z = BMC150_MAGN_REGVAL_TO_REPZ(reg_val);
+ }
+ if (odr <= 0) {
+ ret = bmc150_magn_get_odr(data, &odr);
+ if (ret < 0)
+ return ret;
+ }
+ /* the maximum selectable read-out frequency from datasheet */
+ max_odr = 1000000 / (145 * rep_xy + 500 * rep_z + 980);
+ if (odr > max_odr) {
+ dev_err(&data->client->dev,
+ "Can't set oversampling with sampling freq %d\n",
+ odr);
+ return -EINVAL;
+ }
+ data->max_odr = max_odr;
+
+ return 0;
+}
+
+static s32 bmc150_magn_compensate_x(struct bmc150_magn_trim_regs *tregs, s16 x,
+ u16 rhall)
+{
+ s16 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+ if (x == BMC150_MAGN_XY_OVERFLOW_VAL)
+ return S32_MIN;
+
+ if (!rhall)
+ rhall = xyz1;
+
+ val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+ val = ((s16)((((s32)x) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+ ((s32)val)) >> 7)) + (((s32)val) *
+ ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+ ((s32)(((s16)tregs->x2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+ (((s16)tregs->x1) << 3);
+
+ return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_y(struct bmc150_magn_trim_regs *tregs, s16 y,
+ u16 rhall)
+{
+ s16 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+ if (y == BMC150_MAGN_XY_OVERFLOW_VAL)
+ return S32_MIN;
+
+ if (!rhall)
+ rhall = xyz1;
+
+ val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+ val = ((s16)((((s32)y) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+ ((s32)val)) >> 7)) + (((s32)val) *
+ ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+ ((s32)(((s16)tregs->y2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+ (((s16)tregs->y1) << 3);
+
+ return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_z(struct bmc150_magn_trim_regs *tregs, s16 z,
+ u16 rhall)
+{
+ s32 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+ u16 z1 = le16_to_cpu(tregs->z1);
+ s16 z2 = le16_to_cpu(tregs->z2);
+ s16 z3 = le16_to_cpu(tregs->z3);
+ s16 z4 = le16_to_cpu(tregs->z4);
+
+ if (z == BMC150_MAGN_Z_OVERFLOW_VAL)
+ return S32_MIN;
+
+ val = (((((s32)(z - z4)) << 15) - ((((s32)z3) * ((s32)(((s16)rhall) -
+ ((s16)xyz1)))) >> 2)) / (z2 + ((s16)(((((s32)z1) *
+ ((((s16)rhall) << 1))) + (1 << 15)) >> 16))));
+
+ return val;
+}
+
+static int bmc150_magn_read_xyz(struct bmc150_magn_data *data, s32 *buffer)
+{
+ int ret;
+ __le16 values[AXIS_XYZR_MAX];
+ s16 raw_x, raw_y, raw_z;
+ u16 rhall;
+ struct bmc150_magn_trim_regs tregs;
+
+ ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_X_L,
+ values, sizeof(values));
+ if (ret < 0)
+ return ret;
+
+ raw_x = (s16)le16_to_cpu(values[AXIS_X]) >> BMC150_MAGN_SHIFT_XY_L;
+ raw_y = (s16)le16_to_cpu(values[AXIS_Y]) >> BMC150_MAGN_SHIFT_XY_L;
+ raw_z = (s16)le16_to_cpu(values[AXIS_Z]) >> BMC150_MAGN_SHIFT_Z_L;
+ rhall = le16_to_cpu(values[RHALL]) >> BMC150_MAGN_SHIFT_RHALL_L;
+
+ ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_TRIM_START,
+ &tregs, sizeof(tregs));
+ if (ret < 0)
+ return ret;
+
+ buffer[AXIS_X] = bmc150_magn_compensate_x(&tregs, raw_x, rhall);
+ buffer[AXIS_Y] = bmc150_magn_compensate_y(&tregs, raw_y, rhall);
+ buffer[AXIS_Z] = bmc150_magn_compensate_z(&tregs, raw_z, rhall);
+
+ return 0;
+}
+
+static int bmc150_magn_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret, tmp;
+ s32 values[AXIS_XYZ_MAX];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&data->mutex);
+
+ ret = bmc150_magn_set_power_state(data, true);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ ret = bmc150_magn_read_xyz(data, values);
+ if (ret < 0) {
+ bmc150_magn_set_power_state(data, false);
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ *val = values[chan->scan_index];
+
+ ret = bmc150_magn_set_power_state(data, false);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ mutex_unlock(&data->mutex);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * The API/driver performs an off-chip temperature
+ * compensation and outputs x/y/z magnetic field data in
+ * 16 LSB/uT to the upper application layer.
+ */
+ *val = 0;
+ *val2 = 625;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = bmc150_magn_get_odr(data, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+ &tmp);
+ if (ret < 0)
+ return ret;
+ *val = BMC150_MAGN_REGVAL_TO_REPXY(tmp);
+ return IIO_VAL_INT;
+ case IIO_MOD_Z:
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+ &tmp);
+ if (ret < 0)
+ return ret;
+ *val = BMC150_MAGN_REGVAL_TO_REPZ(tmp);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val > data->max_odr)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_odr(data, val);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ if (val < 1 || val > 511)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_max_odr(data, val, 0, 0);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_REP_XY,
+ BMC150_MAGN_REG_REP_DATAMASK,
+ BMC150_MAGN_REPXY_TO_REGVAL
+ (val));
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_MOD_Z:
+ if (val < 1 || val > 256)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_max_odr(data, 0, val, 0);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_REP_Z,
+ BMC150_MAGN_REG_REP_DATAMASK,
+ BMC150_MAGN_REPZ_TO_REGVAL
+ (val));
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ size_t len = 0;
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+ if (bmc150_magn_samp_freq_table[i].freq > data->max_odr)
+ break;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ bmc150_magn_samp_freq_table[i].freq);
+ }
+ /* replace last space with a newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(bmc150_magn_show_samp_freq_avail);
+
+static struct attribute *bmc150_magn_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group bmc150_magn_attrs_group = {
+ .attrs = bmc150_magn_attributes,
+};
+
+#define BMC150_MAGN_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = AXIS_##_axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_LE \
+ }, \
+}
+
+static const struct iio_chan_spec bmc150_magn_channels[] = {
+ BMC150_MAGN_CHANNEL(X),
+ BMC150_MAGN_CHANNEL(Y),
+ BMC150_MAGN_CHANNEL(Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_info bmc150_magn_info = {
+ .attrs = &bmc150_magn_attrs_group,
+ .read_raw = bmc150_magn_read_raw,
+ .write_raw = bmc150_magn_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const unsigned long bmc150_magn_scan_masks[] = {
+ BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
+ 0};
+
+static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_read_xyz(data, data->buffer);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+
+err:
+ mutex_unlock(&data->mutex);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int bmc150_magn_init(struct bmc150_magn_data *data)
+{
+ int ret, chip_id;
+ struct bmc150_magn_preset preset;
+
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
+ false);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed to bring up device from suspend mode\n");
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed reading chip id\n");
+ goto err_poweroff;
+ }
+ if (chip_id != BMC150_MAGN_CHIP_ID_VAL) {
+ dev_err(&data->client->dev, "Invalid chip id 0x%x\n", chip_id);
+ ret = -ENODEV;
+ goto err_poweroff;
+ }
+ dev_dbg(&data->client->dev, "Chip id %x\n", chip_id);
+
+ preset = bmc150_magn_presets_table[BMC150_MAGN_DEFAULT_PRESET];
+ ret = bmc150_magn_set_odr(data, preset.odr);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set ODR to %d\n",
+ preset.odr);
+ goto err_poweroff;
+ }
+
+ ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_XY,
+ BMC150_MAGN_REPXY_TO_REGVAL(preset.rep_xy));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set REP XY to %d\n",
+ preset.rep_xy);
+ goto err_poweroff;
+ }
+
+ ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_Z,
+ BMC150_MAGN_REPZ_TO_REGVAL(preset.rep_z));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set REP Z to %d\n",
+ preset.rep_z);
+ goto err_poweroff;
+ }
+
+ ret = bmc150_magn_set_max_odr(data, preset.rep_xy, preset.rep_z,
+ preset.odr);
+ if (ret < 0)
+ goto err_poweroff;
+
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to power on device\n");
+ goto err_poweroff;
+ }
+
+ return 0;
+
+err_poweroff:
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ return ret;
+}
+
+static int bmc150_magn_reset_intr(struct bmc150_magn_data *data)
+{
+ int tmp;
+
+ /*
+ * Data Ready (DRDY) is always cleared after
+ * readout of data registers ends.
+ */
+ return regmap_read(data->regmap, BMC150_MAGN_REG_X_L, &tmp);
+}
+
+static int bmc150_magn_trig_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (!data->dready_trigger_on)
+ return 0;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_reset_intr(data);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&data->mutex);
+ if (state == data->dready_trigger_on)
+ goto err_unlock;
+
+ ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
+ BMC150_MAGN_MASK_DRDY_EN,
+ state << BMC150_MAGN_SHIFT_DRDY_EN);
+ if (ret < 0)
+ goto err_unlock;
+
+ data->dready_trigger_on = state;
+
+ if (state) {
+ ret = bmc150_magn_reset_intr(data);
+ if (ret < 0)
+ goto err_unlock;
+ }
+ mutex_unlock(&data->mutex);
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static const struct iio_trigger_ops bmc150_magn_trigger_ops = {
+ .set_trigger_state = bmc150_magn_data_rdy_trigger_set_state,
+ .try_reenable = bmc150_magn_trig_try_reen,
+ .owner = THIS_MODULE,
+};
+
+static int bmc150_magn_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ return bmc150_magn_set_power_state(data, true);
+}
+
+static int bmc150_magn_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ return bmc150_magn_set_power_state(data, false);
+}
+
+static const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = {
+ .preenable = bmc150_magn_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = bmc150_magn_buffer_postdisable,
+};
+
+static const char *bmc150_magn_match_acpi_device(struct device *dev)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+
+ return dev_name(dev);
+}
+
+static int bmc150_magn_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bmc150_magn_data *data;
+ struct iio_dev *indio_dev;
+ const char *name = NULL;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ if (id)
+ name = id->name;
+ else if (ACPI_HANDLE(&client->dev))
+ name = bmc150_magn_match_acpi_device(&client->dev);
+ else
+ return -ENOSYS;
+
+ mutex_init(&data->mutex);
+ data->regmap = devm_regmap_init_i2c(client, &bmc150_magn_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "Failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = bmc150_magn_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = bmc150_magn_channels;
+ indio_dev->num_channels = ARRAY_SIZE(bmc150_magn_channels);
+ indio_dev->available_scan_masks = bmc150_magn_scan_masks;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &bmc150_magn_info;
+
+ if (client->irq > 0) {
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "iio trigger alloc failed\n");
+ goto err_poweroff;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &bmc150_magn_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev, "iio trigger register failed\n");
+ goto err_poweroff;
+ }
+
+ ret = request_threaded_irq(client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ NULL,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ BMC150_MAGN_IRQ_NAME,
+ data->dready_trig);
+ if (ret < 0) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ goto err_trigger_unregister;
+ }
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ bmc150_magn_trigger_handler,
+ &bmc150_magn_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "iio triggered buffer setup failed\n");
+ goto err_free_irq;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto err_buffer_cleanup;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ goto err_iio_unregister;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+
+ return 0;
+
+err_iio_unregister:
+ iio_device_unregister(indio_dev);
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_free_irq:
+ if (client->irq > 0)
+ free_irq(client->irq, data->dready_trig);
+err_trigger_unregister:
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+err_poweroff:
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ return ret;
+}
+
+static int bmc150_magn_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ if (client->irq > 0)
+ free_irq(data->client->irq, data->dready_trig);
+
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+
+ mutex_lock(&data->mutex);
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bmc150_magn_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+ true);
+ mutex_unlock(&data->mutex);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "powering off device failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Should be called with data->mutex held.
+ */
+static int bmc150_magn_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ return bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int bmc150_magn_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+ true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmc150_magn_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops bmc150_magn_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bmc150_magn_suspend, bmc150_magn_resume)
+ SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend,
+ bmc150_magn_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id bmc150_magn_acpi_match[] = {
+ {"BMC150B", 0},
+ {"BMC156B", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
+
+static const struct i2c_device_id bmc150_magn_id[] = {
+ {"bmc150_magn", 0},
+ {"bmc156_magn", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bmc150_magn_id);
+
+static struct i2c_driver bmc150_magn_driver = {
+ .driver = {
+ .name = BMC150_MAGN_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
+ .pm = &bmc150_magn_pm_ops,
+ },
+ .probe = bmc150_magn_probe,
+ .remove = bmc150_magn_remove,
+ .id_table = bmc150_magn_id,
+};
+module_i2c_driver(bmc150_magn_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 magnetometer driver");
diff --git a/kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index 4f9c0be24..d8a0c8da8 100644
--- a/kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/kernel/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -510,7 +510,7 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_magn_3d_ids[] = {
+static const struct platform_device_id hid_magn_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200083",
diff --git a/kernel/drivers/iio/magnetometer/mmc35240.c b/kernel/drivers/iio/magnetometer/mmc35240.c
new file mode 100644
index 000000000..176e14a61
--- /dev/null
+++ b/kernel/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/kernel/drivers/iio/magnetometer/st_magn.h b/kernel/drivers/iio/magnetometer/st_magn.h
index 7e81d00ef..06a4d9c35 100644
--- a/kernel/drivers/iio/magnetometer/st_magn.h
+++ b/kernel/drivers/iio/magnetometer/st_magn.h
@@ -14,9 +14,11 @@
#include <linux/types.h>
#include <linux/iio/common/st_sensors.h>
+#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
+#define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn"
int st_magn_common_probe(struct iio_dev *indio_dev);
void st_magn_common_remove(struct iio_dev *indio_dev);
@@ -24,6 +26,8 @@ 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)
{
diff --git a/kernel/drivers/iio/magnetometer/st_magn_buffer.c b/kernel/drivers/iio/magnetometer/st_magn_buffer.c
index bf427dc0d..ecd3bd0a9 100644
--- a/kernel/drivers/iio/magnetometer/st_magn_buffer.c
+++ b/kernel/drivers/iio/magnetometer/st_magn_buffer.c
@@ -23,6 +23,13 @@
#include <linux/iio/common/st_sensors.h>
#include "st_magn.h"
+int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+
+ return st_sensors_set_dataready_irq(indio_dev, state);
+}
+
static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
{
return st_sensors_set_enable(indio_dev, true);
diff --git a/kernel/drivers/iio/magnetometer/st_magn_core.c b/kernel/drivers/iio/magnetometer/st_magn_core.c
index 2e56f812a..b27f01466 100644
--- a/kernel/drivers/iio/magnetometer/st_magn_core.c
+++ b/kernel/drivers/iio/magnetometer/st_magn_core.c
@@ -43,8 +43,49 @@
#define ST_MAGN_FS_AVL_8000MG 8000
#define ST_MAGN_FS_AVL_8100MG 8100
#define ST_MAGN_FS_AVL_12000MG 12000
+#define ST_MAGN_FS_AVL_15000MG 15000
#define ST_MAGN_FS_AVL_16000MG 16000
+/* CUSTOM VALUES FOR SENSOR 0 */
+#define ST_MAGN_0_ODR_ADDR 0x00
+#define ST_MAGN_0_ODR_MASK 0x1c
+#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
+#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
+#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
+#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
+#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
+#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
+#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
+#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
+#define ST_MAGN_0_PW_ADDR 0x02
+#define ST_MAGN_0_PW_MASK 0x03
+#define ST_MAGN_0_PW_ON 0x00
+#define ST_MAGN_0_PW_OFF 0x03
+#define ST_MAGN_0_FS_ADDR 0x01
+#define ST_MAGN_0_FS_MASK 0xe0
+#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
+#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
+#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
+#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
+#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
+#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
+#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
+#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
+#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
+#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
+#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
+#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
+#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
+#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
+#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
+#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
+#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
+#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
+#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
+#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
+#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
+#define ST_MAGN_0_MULTIREAD_BIT false
+
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_MAGN_1_WAI_EXP 0x3c
#define ST_MAGN_1_ODR_ADDR 0x00
@@ -117,6 +158,29 @@
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_MAGN_3_WAI_ADDR 0x4f
+#define ST_MAGN_3_WAI_EXP 0x40
+#define ST_MAGN_3_ODR_ADDR 0x60
+#define ST_MAGN_3_ODR_MASK 0x0c
+#define ST_MAGN_3_ODR_AVL_10HZ_VAL 0x00
+#define ST_MAGN_3_ODR_AVL_20HZ_VAL 0x01
+#define ST_MAGN_3_ODR_AVL_50HZ_VAL 0x02
+#define ST_MAGN_3_ODR_AVL_100HZ_VAL 0x03
+#define ST_MAGN_3_PW_ADDR 0x60
+#define ST_MAGN_3_PW_MASK 0x03
+#define ST_MAGN_3_PW_ON 0x00
+#define ST_MAGN_3_PW_OFF 0x03
+#define ST_MAGN_3_BDU_ADDR 0x62
+#define ST_MAGN_3_BDU_MASK 0x10
+#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
+#define ST_MAGN_3_DRDY_INT_MASK 0x01
+#define ST_MAGN_3_FS_AVL_15000_GAIN 1500
+#define ST_MAGN_3_MULTIREAD_BIT false
+#define ST_MAGN_3_OUT_X_L_ADDR 0x68
+#define ST_MAGN_3_OUT_Y_L_ADDR 0x6a
+#define ST_MAGN_3_OUT_Z_L_ADDR 0x6c
+
static const struct iio_chan_spec st_magn_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -149,9 +213,103 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3)
};
+static const struct iio_chan_spec st_magn_3_16bit_channels[] = {
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+ ST_MAGN_3_OUT_X_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+ ST_MAGN_3_OUT_Y_L_ADDR),
+ ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+ ST_MAGN_3_OUT_Z_L_ADDR),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
static const struct st_sensor_settings st_magn_sensors_settings[] = {
{
+ .wai = 0, /* This sensor has no valid WhoAmI report 0 */
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LSM303DLH_MAGN_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+ .odr = {
+ .addr = ST_MAGN_0_ODR_ADDR,
+ .mask = ST_MAGN_0_ODR_MASK,
+ .odr_avl = {
+ { 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
+ { 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
+ { 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
+ { 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
+ { 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
+ { 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
+ { 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_MAGN_0_PW_ADDR,
+ .mask = ST_MAGN_0_PW_MASK,
+ .value_on = ST_MAGN_0_PW_ON,
+ .value_off = ST_MAGN_0_PW_OFF,
+ },
+ .fs = {
+ .addr = ST_MAGN_0_FS_ADDR,
+ .mask = ST_MAGN_0_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_MAGN_FS_AVL_1300MG,
+ .value = ST_MAGN_0_FS_AVL_1300_VAL,
+ .gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
+ },
+ [1] = {
+ .num = ST_MAGN_FS_AVL_1900MG,
+ .value = ST_MAGN_0_FS_AVL_1900_VAL,
+ .gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
+ },
+ [2] = {
+ .num = ST_MAGN_FS_AVL_2500MG,
+ .value = ST_MAGN_0_FS_AVL_2500_VAL,
+ .gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
+ },
+ [3] = {
+ .num = ST_MAGN_FS_AVL_4000MG,
+ .value = ST_MAGN_0_FS_AVL_4000_VAL,
+ .gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
+ },
+ [4] = {
+ .num = ST_MAGN_FS_AVL_4700MG,
+ .value = ST_MAGN_0_FS_AVL_4700_VAL,
+ .gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
+ },
+ [5] = {
+ .num = ST_MAGN_FS_AVL_5600MG,
+ .value = ST_MAGN_0_FS_AVL_5600_VAL,
+ .gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
+ },
+ [6] = {
+ .num = ST_MAGN_FS_AVL_8100MG,
+ .value = ST_MAGN_0_FS_AVL_8100_VAL,
+ .gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
+ },
+ },
+ },
+ .multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
+ .bootime = 2,
+ },
+ {
.wai = ST_MAGN_1_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM303DLHC_MAGN_DEV_NAME,
[1] = LSM303DLM_MAGN_DEV_NAME,
@@ -230,6 +388,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
},
{
.wai = ST_MAGN_2_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3MDL_MAGN_DEV_NAME,
},
@@ -283,6 +442,48 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
.bootime = 2,
},
+ {
+ .wai = ST_MAGN_3_WAI_EXP,
+ .wai_addr = ST_MAGN_3_WAI_ADDR,
+ .sensors_supported = {
+ [0] = LSM303AGR_MAGN_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
+ .odr = {
+ .addr = ST_MAGN_3_ODR_ADDR,
+ .mask = ST_MAGN_3_ODR_MASK,
+ .odr_avl = {
+ { 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, },
+ { 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, },
+ { 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, },
+ { 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_MAGN_3_PW_ADDR,
+ .mask = ST_MAGN_3_PW_MASK,
+ .value_on = ST_MAGN_3_PW_ON,
+ .value_off = ST_MAGN_3_PW_OFF,
+ },
+ .fs = {
+ .fs_avl = {
+ [0] = {
+ .num = ST_MAGN_FS_AVL_15000MG,
+ .gain = ST_MAGN_3_FS_AVL_15000_GAIN,
+ },
+ },
+ },
+ .bdu = {
+ .addr = ST_MAGN_3_BDU_ADDR,
+ .mask = ST_MAGN_3_BDU_MASK,
+ },
+ .drdy_irq = {
+ .addr = ST_MAGN_3_DRDY_IRQ_ADDR,
+ .mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
+ },
+ .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
+ .bootime = 2,
+ },
};
static int st_magn_read_raw(struct iio_dev *indio_dev,
@@ -359,7 +560,18 @@ static const struct iio_info magn_info = {
.attrs = &st_magn_attribute_group,
.read_raw = &st_magn_read_raw,
.write_raw = &st_magn_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_magn_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = ST_MAGN_TRIGGER_SET_STATE,
};
+#define ST_MAGN_TRIGGER_OPS (&st_magn_trigger_ops)
+#else
+#define ST_MAGN_TRIGGER_OPS NULL
+#endif
int st_magn_common_probe(struct iio_dev *indio_dev)
{
@@ -397,7 +609,8 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
return err;
if (irq > 0) {
- err = st_sensors_allocate_trigger(indio_dev, NULL);
+ err = st_sensors_allocate_trigger(indio_dev,
+ ST_MAGN_TRIGGER_OPS);
if (err < 0)
goto st_magn_probe_trigger_error;
}
diff --git a/kernel/drivers/iio/magnetometer/st_magn_i2c.c b/kernel/drivers/iio/magnetometer/st_magn_i2c.c
index 92e5c1545..8aa37af30 100644
--- a/kernel/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/kernel/drivers/iio/magnetometer/st_magn_i2c.c
@@ -21,6 +21,10 @@
#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,
},
@@ -32,6 +36,10 @@ 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,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
@@ -71,16 +79,17 @@ static int st_magn_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id st_magn_id_table[] = {
+ { LSM303DLH_MAGN_DEV_NAME },
{ LSM303DLHC_MAGN_DEV_NAME },
{ LSM303DLM_MAGN_DEV_NAME },
{ LIS3MDL_MAGN_DEV_NAME },
+ { LSM303AGR_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
static struct i2c_driver st_magn_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "st-magn-i2c",
.of_match_table = of_match_ptr(st_magn_of_match),
},
diff --git a/kernel/drivers/iio/magnetometer/st_magn_spi.c b/kernel/drivers/iio/magnetometer/st_magn_spi.c
index 7adacf160..6325e7dc8 100644
--- a/kernel/drivers/iio/magnetometer/st_magn_spi.c
+++ b/kernel/drivers/iio/magnetometer/st_magn_spi.c
@@ -51,13 +51,13 @@ static const struct spi_device_id st_magn_id_table[] = {
{ LSM303DLHC_MAGN_DEV_NAME },
{ LSM303DLM_MAGN_DEV_NAME },
{ LIS3MDL_MAGN_DEV_NAME },
+ { LSM303AGR_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
static struct spi_driver st_magn_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "st-magn-spi",
},
.probe = st_magn_spi_probe,