diff options
Diffstat (limited to 'drivers/iio/imu/fxos8700_core.c')
-rw-r--r-- | drivers/iio/imu/fxos8700_core.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c new file mode 100644 index 0000000000..6d189c4b9f --- /dev/null +++ b/drivers/iio/imu/fxos8700_core.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FXOS8700 - NXP IMU (accelerometer plus magnetometer) + * + * IIO core driver for FXOS8700, with support for I2C/SPI busses + * + * TODO: Buffer, trigger, and IRQ support + */ +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include "fxos8700.h" + +/* Register Definitions */ +#define FXOS8700_STATUS 0x00 +#define FXOS8700_OUT_X_MSB 0x01 +#define FXOS8700_OUT_X_LSB 0x02 +#define FXOS8700_OUT_Y_MSB 0x03 +#define FXOS8700_OUT_Y_LSB 0x04 +#define FXOS8700_OUT_Z_MSB 0x05 +#define FXOS8700_OUT_Z_LSB 0x06 +#define FXOS8700_F_SETUP 0x09 +#define FXOS8700_TRIG_CFG 0x0a +#define FXOS8700_SYSMOD 0x0b +#define FXOS8700_INT_SOURCE 0x0c +#define FXOS8700_WHO_AM_I 0x0d +#define FXOS8700_XYZ_DATA_CFG 0x0e +#define FXOS8700_HP_FILTER_CUTOFF 0x0f +#define FXOS8700_PL_STATUS 0x10 +#define FXOS8700_PL_CFG 0x11 +#define FXOS8700_PL_COUNT 0x12 +#define FXOS8700_PL_BF_ZCOMP 0x13 +#define FXOS8700_PL_THS_REG 0x14 +#define FXOS8700_A_FFMT_CFG 0x15 +#define FXOS8700_A_FFMT_SRC 0x16 +#define FXOS8700_A_FFMT_THS 0x17 +#define FXOS8700_A_FFMT_COUNT 0x18 +#define FXOS8700_TRANSIENT_CFG 0x1d +#define FXOS8700_TRANSIENT_SRC 0x1e +#define FXOS8700_TRANSIENT_THS 0x1f +#define FXOS8700_TRANSIENT_COUNT 0x20 +#define FXOS8700_PULSE_CFG 0x21 +#define FXOS8700_PULSE_SRC 0x22 +#define FXOS8700_PULSE_THSX 0x23 +#define FXOS8700_PULSE_THSY 0x24 +#define FXOS8700_PULSE_THSZ 0x25 +#define FXOS8700_PULSE_TMLT 0x26 +#define FXOS8700_PULSE_LTCY 0x27 +#define FXOS8700_PULSE_WIND 0x28 +#define FXOS8700_ASLP_COUNT 0x29 +#define FXOS8700_CTRL_REG1 0x2a +#define FXOS8700_CTRL_REG2 0x2b +#define FXOS8700_CTRL_REG3 0x2c +#define FXOS8700_CTRL_REG4 0x2d +#define FXOS8700_CTRL_REG5 0x2e +#define FXOS8700_OFF_X 0x2f +#define FXOS8700_OFF_Y 0x30 +#define FXOS8700_OFF_Z 0x31 +#define FXOS8700_M_DR_STATUS 0x32 +#define FXOS8700_M_OUT_X_MSB 0x33 +#define FXOS8700_M_OUT_X_LSB 0x34 +#define FXOS8700_M_OUT_Y_MSB 0x35 +#define FXOS8700_M_OUT_Y_LSB 0x36 +#define FXOS8700_M_OUT_Z_MSB 0x37 +#define FXOS8700_M_OUT_Z_LSB 0x38 +#define FXOS8700_CMP_X_MSB 0x39 +#define FXOS8700_CMP_X_LSB 0x3a +#define FXOS8700_CMP_Y_MSB 0x3b +#define FXOS8700_CMP_Y_LSB 0x3c +#define FXOS8700_CMP_Z_MSB 0x3d +#define FXOS8700_CMP_Z_LSB 0x3e +#define FXOS8700_M_OFF_X_MSB 0x3f +#define FXOS8700_M_OFF_X_LSB 0x40 +#define FXOS8700_M_OFF_Y_MSB 0x41 +#define FXOS8700_M_OFF_Y_LSB 0x42 +#define FXOS8700_M_OFF_Z_MSB 0x43 +#define FXOS8700_M_OFF_Z_LSB 0x44 +#define FXOS8700_MAX_X_MSB 0x45 +#define FXOS8700_MAX_X_LSB 0x46 +#define FXOS8700_MAX_Y_MSB 0x47 +#define FXOS8700_MAX_Y_LSB 0x48 +#define FXOS8700_MAX_Z_MSB 0x49 +#define FXOS8700_MAX_Z_LSB 0x4a +#define FXOS8700_MIN_X_MSB 0x4b +#define FXOS8700_MIN_X_LSB 0x4c +#define FXOS8700_MIN_Y_MSB 0x4d +#define FXOS8700_MIN_Y_LSB 0x4e +#define FXOS8700_MIN_Z_MSB 0x4f +#define FXOS8700_MIN_Z_LSB 0x50 +#define FXOS8700_TEMP 0x51 +#define FXOS8700_M_THS_CFG 0x52 +#define FXOS8700_M_THS_SRC 0x53 +#define FXOS8700_M_THS_X_MSB 0x54 +#define FXOS8700_M_THS_X_LSB 0x55 +#define FXOS8700_M_THS_Y_MSB 0x56 +#define FXOS8700_M_THS_Y_LSB 0x57 +#define FXOS8700_M_THS_Z_MSB 0x58 +#define FXOS8700_M_THS_Z_LSB 0x59 +#define FXOS8700_M_THS_COUNT 0x5a +#define FXOS8700_M_CTRL_REG1 0x5b +#define FXOS8700_M_CTRL_REG2 0x5c +#define FXOS8700_M_CTRL_REG3 0x5d +#define FXOS8700_M_INT_SRC 0x5e +#define FXOS8700_A_VECM_CFG 0x5f +#define FXOS8700_A_VECM_THS_MSB 0x60 +#define FXOS8700_A_VECM_THS_LSB 0x61 +#define FXOS8700_A_VECM_CNT 0x62 +#define FXOS8700_A_VECM_INITX_MSB 0x63 +#define FXOS8700_A_VECM_INITX_LSB 0x64 +#define FXOS8700_A_VECM_INITY_MSB 0x65 +#define FXOS8700_A_VECM_INITY_LSB 0x66 +#define FXOS8700_A_VECM_INITZ_MSB 0x67 +#define FXOS8700_A_VECM_INITZ_LSB 0x68 +#define FXOS8700_M_VECM_CFG 0x69 +#define FXOS8700_M_VECM_THS_MSB 0x6a +#define FXOS8700_M_VECM_THS_LSB 0x6b +#define FXOS8700_M_VECM_CNT 0x6c +#define FXOS8700_M_VECM_INITX_MSB 0x6d +#define FXOS8700_M_VECM_INITX_LSB 0x6e +#define FXOS8700_M_VECM_INITY_MSB 0x6f +#define FXOS8700_M_VECM_INITY_LSB 0x70 +#define FXOS8700_M_VECM_INITZ_MSB 0x71 +#define FXOS8700_M_VECM_INITZ_LSB 0x72 +#define FXOS8700_A_FFMT_THS_X_MSB 0x73 +#define FXOS8700_A_FFMT_THS_X_LSB 0x74 +#define FXOS8700_A_FFMT_THS_Y_MSB 0x75 +#define FXOS8700_A_FFMT_THS_Y_LSB 0x76 +#define FXOS8700_A_FFMT_THS_Z_MSB 0x77 +#define FXOS8700_A_FFMT_THS_Z_LSB 0x78 +#define FXOS8700_A_TRAN_INIT_MSB 0x79 +#define FXOS8700_A_TRAN_INIT_LSB_X 0x7a +#define FXOS8700_A_TRAN_INIT_LSB_Y 0x7b +#define FXOS8700_A_TRAN_INIT_LSB_Z 0x7d +#define FXOS8700_TM_NVM_LOCK 0x7e +#define FXOS8700_NVM_DATA0_35 0x80 +#define FXOS8700_NVM_DATA_BNK3 0xa4 +#define FXOS8700_NVM_DATA_BNK2 0xa5 +#define FXOS8700_NVM_DATA_BNK1 0xa6 +#define FXOS8700_NVM_DATA_BNK0 0xa7 + +/* Bit definitions for FXOS8700_CTRL_REG1 */ +#define FXOS8700_CTRL_ODR_MAX 0x00 +#define FXOS8700_CTRL_ODR_MSK GENMASK(5, 3) + +/* Bit definitions for FXOS8700_M_CTRL_REG1 */ +#define FXOS8700_HMS_MASK GENMASK(1, 0) +#define FXOS8700_OS_MASK GENMASK(4, 2) + +/* Bit definitions for FXOS8700_M_CTRL_REG2 */ +#define FXOS8700_MAXMIN_RST BIT(2) +#define FXOS8700_MAXMIN_DIS_THS BIT(3) +#define FXOS8700_MAXMIN_DIS BIT(4) + +#define FXOS8700_ACTIVE 0x01 +#define FXOS8700_ACTIVE_MIN_USLEEP 4000 /* from table 6 in datasheet */ + +#define FXOS8700_DEVICE_ID 0xC7 +#define FXOS8700_PRE_DEVICE_ID 0xC4 +#define FXOS8700_DATA_BUF_SIZE 3 + +struct fxos8700_data { + struct regmap *regmap; + struct iio_trigger *trig; + __be16 buf[FXOS8700_DATA_BUF_SIZE] __aligned(IIO_DMA_MINALIGN); +}; + +/* Regmap info */ +static const struct regmap_range read_range[] = { + { + .range_min = FXOS8700_STATUS, + .range_max = FXOS8700_A_FFMT_COUNT, + }, { + .range_min = FXOS8700_TRANSIENT_CFG, + .range_max = FXOS8700_A_FFMT_THS_Z_LSB, + }, +}; + +static const struct regmap_range write_range[] = { + { + .range_min = FXOS8700_F_SETUP, + .range_max = FXOS8700_TRIG_CFG, + }, { + .range_min = FXOS8700_XYZ_DATA_CFG, + .range_max = FXOS8700_HP_FILTER_CUTOFF, + }, { + .range_min = FXOS8700_PL_CFG, + .range_max = FXOS8700_A_FFMT_CFG, + }, { + .range_min = FXOS8700_A_FFMT_THS, + .range_max = FXOS8700_TRANSIENT_CFG, + }, { + .range_min = FXOS8700_TRANSIENT_THS, + .range_max = FXOS8700_PULSE_CFG, + }, { + .range_min = FXOS8700_PULSE_THSX, + .range_max = FXOS8700_OFF_Z, + }, { + .range_min = FXOS8700_M_OFF_X_MSB, + .range_max = FXOS8700_M_OFF_Z_LSB, + }, { + .range_min = FXOS8700_M_THS_CFG, + .range_max = FXOS8700_M_THS_CFG, + }, { + .range_min = FXOS8700_M_THS_X_MSB, + .range_max = FXOS8700_M_CTRL_REG3, + }, { + .range_min = FXOS8700_A_VECM_CFG, + .range_max = FXOS8700_A_FFMT_THS_Z_LSB, + }, +}; + +static const struct regmap_access_table driver_read_table = { + .yes_ranges = read_range, + .n_yes_ranges = ARRAY_SIZE(read_range), +}; + +static const struct regmap_access_table driver_write_table = { + .yes_ranges = write_range, + .n_yes_ranges = ARRAY_SIZE(write_range), +}; + +const struct regmap_config fxos8700_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FXOS8700_NVM_DATA_BNK0, + .rd_table = &driver_read_table, + .wr_table = &driver_write_table, +}; +EXPORT_SYMBOL(fxos8700_regmap_config); + +#define FXOS8700_CHANNEL(_type, _axis) { \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +enum fxos8700_accel_scale_bits { + MODE_2G = 0, + MODE_4G, + MODE_8G, +}; + +/* scan indexes follow DATA register order */ +enum fxos8700_scan_axis { + FXOS8700_SCAN_ACCEL_X = 0, + FXOS8700_SCAN_ACCEL_Y, + FXOS8700_SCAN_ACCEL_Z, + FXOS8700_SCAN_MAGN_X, + FXOS8700_SCAN_MAGN_Y, + FXOS8700_SCAN_MAGN_Z, + FXOS8700_SCAN_RHALL, + FXOS8700_SCAN_TIMESTAMP, +}; + +enum fxos8700_sensor { + FXOS8700_ACCEL = 0, + FXOS8700_MAGN, + FXOS8700_NUM_SENSORS /* must be last */ +}; + +enum fxos8700_int_pin { + FXOS8700_PIN_INT1, + FXOS8700_PIN_INT2 +}; + +struct fxos8700_scale { + u8 bits; + int uscale; +}; + +struct fxos8700_odr { + u8 bits; + int odr; + int uodr; +}; + +static const struct fxos8700_scale fxos8700_accel_scale[] = { + { MODE_2G, 244}, + { MODE_4G, 488}, + { MODE_8G, 976}, +}; + +/* + * Accellerometer and magnetometer have the same ODR options, set in the + * CTRL_REG1 register. ODR is halved when using both sensors at once in + * hybrid mode. + */ +static const struct fxos8700_odr fxos8700_odr[] = { + {0x00, 800, 0}, + {0x01, 400, 0}, + {0x02, 200, 0}, + {0x03, 100, 0}, + {0x04, 50, 0}, + {0x05, 12, 500000}, + {0x06, 6, 250000}, + {0x07, 1, 562500}, +}; + +static const struct iio_chan_spec fxos8700_channels[] = { + FXOS8700_CHANNEL(IIO_ACCEL, X), + FXOS8700_CHANNEL(IIO_ACCEL, Y), + FXOS8700_CHANNEL(IIO_ACCEL, Z), + FXOS8700_CHANNEL(IIO_MAGN, X), + FXOS8700_CHANNEL(IIO_MAGN, Y), + FXOS8700_CHANNEL(IIO_MAGN, Z), + IIO_CHAN_SOFT_TIMESTAMP(FXOS8700_SCAN_TIMESTAMP), +}; + +static enum fxos8700_sensor fxos8700_to_sensor(enum iio_chan_type iio_type) +{ + switch (iio_type) { + case IIO_ACCEL: + return FXOS8700_ACCEL; + case IIO_MAGN: + return FXOS8700_MAGN; + default: + return -EINVAL; + } +} + +static int fxos8700_set_active_mode(struct fxos8700_data *data, + enum fxos8700_sensor t, bool mode) +{ + int ret; + + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, mode); + if (ret) + return ret; + + usleep_range(FXOS8700_ACTIVE_MIN_USLEEP, + FXOS8700_ACTIVE_MIN_USLEEP + 1000); + + return 0; +} + +static int fxos8700_set_scale(struct fxos8700_data *data, + enum fxos8700_sensor t, int uscale) +{ + int i, ret, val; + bool active_mode; + static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale); + struct device *dev = regmap_get_device(data->regmap); + + if (t == FXOS8700_MAGN) { + dev_err(dev, "Magnetometer scale is locked at 0.001Gs\n"); + return -EINVAL; + } + + /* + * When device is in active mode, it failed to set an ACCEL + * full-scale range(2g/4g/8g) in FXOS8700_XYZ_DATA_CFG. + * This is not align with the datasheet, but it is a fxos8700 + * chip behavier. Set the device in standby mode before setting + * an ACCEL full-scale range. + */ + ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val); + if (ret) + return ret; + + active_mode = val & FXOS8700_ACTIVE; + if (active_mode) { + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, + val & ~FXOS8700_ACTIVE); + if (ret) + return ret; + } + + for (i = 0; i < scale_num; i++) + if (fxos8700_accel_scale[i].uscale == uscale) + break; + + if (i == scale_num) + return -EINVAL; + + ret = regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, + fxos8700_accel_scale[i].bits); + if (ret) + return ret; + return regmap_write(data->regmap, FXOS8700_CTRL_REG1, + active_mode); +} + +static int fxos8700_get_scale(struct fxos8700_data *data, + enum fxos8700_sensor t, int *uscale) +{ + int i, ret, val; + static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale); + + if (t == FXOS8700_MAGN) { + *uscale = 1000; /* Magnetometer is locked at 0.001Gs */ + return 0; + } + + ret = regmap_read(data->regmap, FXOS8700_XYZ_DATA_CFG, &val); + if (ret) + return ret; + + for (i = 0; i < scale_num; i++) { + if (fxos8700_accel_scale[i].bits == (val & 0x3)) { + *uscale = fxos8700_accel_scale[i].uscale; + return 0; + } + } + + return -EINVAL; +} + +static int fxos8700_get_data(struct fxos8700_data *data, int chan_type, + int axis, int *val) +{ + u8 base, reg; + s16 tmp; + int ret; + + /* + * Different register base addresses varies with channel types. + * This bug hasn't been noticed before because using an enum is + * really hard to read. Use an a switch statement to take over that. + */ + switch (chan_type) { + case IIO_ACCEL: + base = FXOS8700_OUT_X_MSB; + break; + case IIO_MAGN: + base = FXOS8700_M_OUT_X_MSB; + break; + default: + return -EINVAL; + } + + /* Block read 6 bytes of device output registers to avoid data loss */ + ret = regmap_bulk_read(data->regmap, base, data->buf, + sizeof(data->buf)); + if (ret) + return ret; + + /* Convert axis to buffer index */ + reg = axis - IIO_MOD_X; + + /* + * Convert to native endianness. The accel data and magn data + * are signed, so a forced type conversion is needed. + */ + tmp = be16_to_cpu(data->buf[reg]); + + /* + * ACCEL output data registers contain the X-axis, Y-axis, and Z-axis + * 14-bit left-justified sample data and MAGN output data registers + * contain the X-axis, Y-axis, and Z-axis 16-bit sample data. Apply + * a signed 2 bits right shift to the readback raw data from ACCEL + * output data register and keep that from MAGN sensor as the origin. + * Value should be extended to 32 bit. + */ + switch (chan_type) { + case IIO_ACCEL: + tmp = tmp >> 2; + break; + case IIO_MAGN: + /* Nothing to do */ + break; + default: + return -EINVAL; + } + + /* Convert to native endianness */ + *val = sign_extend32(tmp, 15); + + return 0; +} + +static int fxos8700_set_odr(struct fxos8700_data *data, enum fxos8700_sensor t, + int odr, int uodr) +{ + int i, ret, val; + bool active_mode; + static const int odr_num = ARRAY_SIZE(fxos8700_odr); + + ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val); + if (ret) + return ret; + + active_mode = val & FXOS8700_ACTIVE; + + if (active_mode) { + /* + * The device must be in standby mode to change any of the + * other fields within CTRL_REG1 + */ + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, + val & ~FXOS8700_ACTIVE); + if (ret) + return ret; + } + + for (i = 0; i < odr_num; i++) + if (fxos8700_odr[i].odr == odr && fxos8700_odr[i].uodr == uodr) + break; + + if (i >= odr_num) + return -EINVAL; + + val &= ~FXOS8700_CTRL_ODR_MSK; + val |= FIELD_PREP(FXOS8700_CTRL_ODR_MSK, fxos8700_odr[i].bits) | FXOS8700_ACTIVE; + return regmap_write(data->regmap, FXOS8700_CTRL_REG1, val); +} + +static int fxos8700_get_odr(struct fxos8700_data *data, enum fxos8700_sensor t, + int *odr, int *uodr) +{ + int i, val, ret; + static const int odr_num = ARRAY_SIZE(fxos8700_odr); + + ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val); + if (ret) + return ret; + + val = FIELD_GET(FXOS8700_CTRL_ODR_MSK, val); + + for (i = 0; i < odr_num; i++) + if (val == fxos8700_odr[i].bits) + break; + + if (i >= odr_num) + return -EINVAL; + + *odr = fxos8700_odr[i].odr; + *uodr = fxos8700_odr[i].uodr; + + return 0; +} + +static int fxos8700_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct fxos8700_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = fxos8700_get_data(data, chan->type, chan->channel2, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + ret = fxos8700_get_scale(data, fxos8700_to_sensor(chan->type), + val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = fxos8700_get_odr(data, fxos8700_to_sensor(chan->type), + val, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int fxos8700_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct fxos8700_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return fxos8700_set_scale(data, fxos8700_to_sensor(chan->type), + val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return fxos8700_set_odr(data, fxos8700_to_sensor(chan->type), + val, val2); + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(in_accel_sampling_frequency_available, + "1.5625 6.25 12.5 50 100 200 400 800"); +static IIO_CONST_ATTR(in_magn_sampling_frequency_available, + "1.5625 6.25 12.5 50 100 200 400 800"); +static IIO_CONST_ATTR(in_accel_scale_available, "0.000244 0.000488 0.000976"); +static IIO_CONST_ATTR(in_magn_scale_available, "0.001000"); + +static struct attribute *fxos8700_attrs[] = { + &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_magn_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + &iio_const_attr_in_magn_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group fxos8700_attrs_group = { + .attrs = fxos8700_attrs, +}; + +static const struct iio_info fxos8700_info = { + .read_raw = fxos8700_read_raw, + .write_raw = fxos8700_write_raw, + .attrs = &fxos8700_attrs_group, +}; + +static int fxos8700_chip_init(struct fxos8700_data *data, bool use_spi) +{ + int ret; + unsigned int val; + struct device *dev = regmap_get_device(data->regmap); + + ret = regmap_read(data->regmap, FXOS8700_WHO_AM_I, &val); + if (ret) { + dev_err(dev, "Error reading chip id\n"); + return ret; + } + if (val != FXOS8700_DEVICE_ID && val != FXOS8700_PRE_DEVICE_ID) { + dev_err(dev, "Wrong chip id, got %x expected %x or %x\n", + val, FXOS8700_DEVICE_ID, FXOS8700_PRE_DEVICE_ID); + return -ENODEV; + } + + ret = fxos8700_set_active_mode(data, FXOS8700_ACCEL, true); + if (ret) + return ret; + + ret = fxos8700_set_active_mode(data, FXOS8700_MAGN, true); + if (ret) + return ret; + + /* + * The device must be in standby mode to change any of the other fields + * within CTRL_REG1 + */ + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, 0x00); + if (ret) + return ret; + + /* Set max oversample ratio (OSR) and both devices active */ + ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG1, + FXOS8700_HMS_MASK | FXOS8700_OS_MASK); + if (ret) + return ret; + + /* Disable and rst min/max measurements & threshold */ + ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG2, + FXOS8700_MAXMIN_RST | FXOS8700_MAXMIN_DIS_THS | + FXOS8700_MAXMIN_DIS); + if (ret) + return ret; + + /* + * Set max full-scale range (+/-8G) for ACCEL sensor in chip + * initialization then activate the device. + */ + ret = regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G); + if (ret) + return ret; + + /* Max ODR (800Hz individual or 400Hz hybrid), active mode */ + return regmap_update_bits(data->regmap, FXOS8700_CTRL_REG1, + FXOS8700_CTRL_ODR_MSK | FXOS8700_ACTIVE, + FIELD_PREP(FXOS8700_CTRL_ODR_MSK, FXOS8700_CTRL_ODR_MAX) | + FXOS8700_ACTIVE); +} + +static void fxos8700_chip_uninit(void *data) +{ + struct fxos8700_data *fxos8700_data = data; + + fxos8700_set_active_mode(fxos8700_data, FXOS8700_ACCEL, false); + fxos8700_set_active_mode(fxos8700_data, FXOS8700_MAGN, false); +} + +int fxos8700_core_probe(struct device *dev, struct regmap *regmap, + const char *name, bool use_spi) +{ + struct iio_dev *indio_dev; + struct fxos8700_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->regmap = regmap; + + ret = fxos8700_chip_init(data, use_spi); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, fxos8700_chip_uninit, data); + if (ret) + return ret; + + indio_dev->channels = fxos8700_channels; + indio_dev->num_channels = ARRAY_SIZE(fxos8700_channels); + indio_dev->name = name ? name : "fxos8700"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &fxos8700_info; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(fxos8700_core_probe); + +MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>"); +MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver"); +MODULE_LICENSE("GPL v2"); |