Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Robert Jones | 2585 | 99.81% | 1 | 50.00% |
Jonathan Cameron | 5 | 0.19% | 1 | 50.00% |
Total | 2590 | 2 |
// 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/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_MSK 0x38 #define FXOS8700_CTRL_ODR_MAX 0x00 #define FXOS8700_CTRL_ODR_MIN GENMASK(4, 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_ANGL_VEL: 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; 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 1200uT\n"); return -EINVAL; } for (i = 0; i < scale_num; i++) if (fxos8700_accel_scale[i].uscale == uscale) break; if (i == scale_num) return -EINVAL; return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, fxos8700_accel_scale[i].bits); } 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 = 1200; /* Magnetometer is locked at 1200uT */ 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; int ret; enum fxos8700_sensor type = fxos8700_to_sensor(chan_type); base = type ? FXOS8700_OUT_X_MSB : FXOS8700_M_OUT_X_MSB; /* Block read 6 bytes of device output registers to avoid data loss */ ret = regmap_bulk_read(data->regmap, base, data->buf, FXOS8700_DATA_BUF_SIZE); if (ret) return ret; /* Convert axis to buffer index */ reg = axis - IIO_MOD_X; /* Convert to native endianness */ *val = sign_extend32(be16_to_cpu(data->buf[reg]), 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; return regmap_update_bits(data->regmap, FXOS8700_CTRL_REG1, FXOS8700_CTRL_ODR_MSK + FXOS8700_ACTIVE, fxos8700_odr[i].bits << 3 | active_mode); } 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 &= FXOS8700_CTRL_ODR_MSK; 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.000001200"); 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; /* Max ODR (800Hz individual or 400Hz hybrid), active mode */ ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, FXOS8700_CTRL_ODR_MAX | FXOS8700_ACTIVE); if (ret) return ret; /* Set for max full-scale range (+/-8G) */ return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G); } 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1