From 4c3c1f2dc7d1c508947a30c743c26ec5d4cc848d Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 28 Feb 2020 10:49:53 -0800 Subject: [PATCH] kernel: 5.4: backport fxos8700 accel support from 5.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport kernel module from 5.5 for FXOS8700CQ, which is a small, low-power, 3-axis linear accelerometer and 3-axis magnetometer combined into a single package. The device features a selectable I2C or point-to-point SPI serial interface with 14-bit accelerometer and 16-bit magnetometer ADC resolution along with smart-embedded functions. Signed-off-by: Tim Harvey [added commit description] Signed-off-by: Petr Štetiar --- ...imu-Add-support-for-the-FXOS8700-IMU.patch | 912 ++++++++++++++++++ 1 file changed, 912 insertions(+) create mode 100644 target/linux/generic/backport-5.4/800-v5.5-iio-imu-Add-support-for-the-FXOS8700-IMU.patch diff --git a/target/linux/generic/backport-5.4/800-v5.5-iio-imu-Add-support-for-the-FXOS8700-IMU.patch b/target/linux/generic/backport-5.4/800-v5.5-iio-imu-Add-support-for-the-FXOS8700-IMU.patch new file mode 100644 index 0000000000..59435e99de --- /dev/null +++ b/target/linux/generic/backport-5.4/800-v5.5-iio-imu-Add-support-for-the-FXOS8700-IMU.patch @@ -0,0 +1,912 @@ +From 84e5ddd5c46ea3bf0cad670da32028994cad5936 Mon Sep 17 00:00:00 2001 +From: Robert Jones +Date: Mon, 14 Oct 2019 11:49:21 -0700 +Subject: [PATCH] iio: imu: Add support for the FXOS8700 IMU +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +FXOS8700CQ is a small, low-power, 3-axis linear accelerometer and 3-axis +magnetometer combined into a single package. The device features a +selectable I2C or point-to-point SPI serial interface with 14-bit +accelerometer and 16-bit magnetometer ADC resolution along with +smart-embedded functions. + +FXOS8700CQ has dynamically selectable accelerationfull-scale ranges of +±2 g/±4 g/±8 g and a fixed magnetic measurement range of ±1200 μT. +Output data rates (ODR) from 1.563 Hz to 800 Hz are selectable by the user +for each sensor. Interleaved magnetic and acceleration data is available +at ODR rates of up to 400 Hz. FXOS8700CQ is available in a plastic QFN +package and it is guaranteed to operate over the extended temperature +range of –40 °C to +85 °C. + +TODO: Trigger and IRQ configuration support + +Datasheet: + http://cache.freescale.com/files/sensors/doc/data_sheet/FXOS8700CQ.pdf + +Signed-off-by: Robert Jones +Signed-off-by: Jonathan Cameron +--- + drivers/iio/imu/Kconfig | 27 ++ + drivers/iio/imu/Makefile | 5 + + drivers/iio/imu/fxos8700.h | 10 + + drivers/iio/imu/fxos8700_core.c | 649 ++++++++++++++++++++++++++++++++++++++++ + drivers/iio/imu/fxos8700_i2c.c | 71 +++++ + drivers/iio/imu/fxos8700_spi.c | 59 ++++ + 6 files changed, 821 insertions(+) + create mode 100644 drivers/iio/imu/fxos8700.h + create mode 100644 drivers/iio/imu/fxos8700_core.c + create mode 100644 drivers/iio/imu/fxos8700_i2c.c + create mode 100644 drivers/iio/imu/fxos8700_spi.c + +diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig +index f3c7282..60bb102 100644 +--- a/drivers/iio/imu/Kconfig ++++ b/drivers/iio/imu/Kconfig +@@ -40,6 +40,33 @@ config ADIS16480 + + source "drivers/iio/imu/bmi160/Kconfig" + ++config FXOS8700 ++ tristate ++ ++config FXOS8700_I2C ++ tristate "NXP FXOS8700 I2C driver" ++ depends on I2C ++ select FXOS8700 ++ select REGMAP_I2C ++ help ++ Say yes here to build support for the NXP FXOS8700 m+g combo ++ sensor on I2C. ++ ++ This driver can also be built as a module. If so, the module will be ++ called fxos8700_i2c. ++ ++config FXOS8700_SPI ++ tristate "NXP FXOS8700 SPI driver" ++ depends on SPI ++ select FXOS8700 ++ select REGMAP_SPI ++ help ++ Say yes here to build support for the NXP FXOS8700 m+g combo ++ sensor on SPI. ++ ++ This driver can also be built as a module. If so, the module will be ++ called fxos8700_spi. ++ + config KMX61 + tristate "Kionix KMX61 6-axis accelerometer and magnetometer" + depends on I2C +diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile +index 4a69588..5237fd4 100644 +--- a/drivers/iio/imu/Makefile ++++ b/drivers/iio/imu/Makefile +@@ -14,6 +14,11 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o + obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o + + obj-y += bmi160/ ++ ++obj-$(CONFIG_FXOS8700) += fxos8700_core.o ++obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o ++obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o ++ + obj-y += inv_mpu6050/ + + obj-$(CONFIG_KMX61) += kmx61.o +diff --git a/drivers/iio/imu/fxos8700.h b/drivers/iio/imu/fxos8700.h +new file mode 100644 +index 00000000..6dfb8d7 +--- /dev/null ++++ b/drivers/iio/imu/fxos8700.h +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef FXOS8700_H_ ++#define FXOS8700_H_ ++ ++extern const struct regmap_config fxos8700_regmap_config; ++ ++int fxos8700_core_probe(struct device *dev, struct regmap *regmap, ++ const char *name, bool use_spi); ++ ++#endif /* FXOS8700_H_ */ +diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c +new file mode 100644 +index 00000000..7b47be4 +--- /dev/null ++++ b/drivers/iio/imu/fxos8700_core.c +@@ -0,0 +1,649 @@ ++// 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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] ____cacheline_aligned; ++}; ++ ++/* 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->dev.parent = dev; ++ 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 "); ++MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/imu/fxos8700_i2c.c b/drivers/iio/imu/fxos8700_i2c.c +new file mode 100644 +index 00000000..3ceb763 +--- /dev/null ++++ b/drivers/iio/imu/fxos8700_i2c.c +@@ -0,0 +1,71 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * FXOS8700 - NXP IMU, I2C bits ++ * ++ * 7-bit I2C slave address determined by SA1 and SA0 logic level ++ * inputs represented in the following table: ++ * SA1 | SA0 | Slave Address ++ * 0 | 0 | 0x1E ++ * 0 | 1 | 0x1D ++ * 1 | 0 | 0x1C ++ * 1 | 1 | 0x1F ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "fxos8700.h" ++ ++static int fxos8700_i2c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct regmap *regmap; ++ const char *name = NULL; ++ ++ regmap = devm_regmap_init_i2c(client, &fxos8700_regmap_config); ++ if (IS_ERR(regmap)) { ++ dev_err(&client->dev, "Failed to register i2c regmap %d\n", ++ (int)PTR_ERR(regmap)); ++ return PTR_ERR(regmap); ++ } ++ ++ if (id) ++ name = id->name; ++ ++ return fxos8700_core_probe(&client->dev, regmap, name, false); ++} ++ ++static const struct i2c_device_id fxos8700_i2c_id[] = { ++ {"fxos8700", 0}, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, fxos8700_i2c_id); ++ ++static const struct acpi_device_id fxos8700_acpi_match[] = { ++ {"FXOS8700", 0}, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match); ++ ++static const struct of_device_id fxos8700_of_match[] = { ++ { .compatible = "nxp,fxos8700" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, fxos8700_of_match); ++ ++static struct i2c_driver fxos8700_i2c_driver = { ++ .driver = { ++ .name = "fxos8700_i2c", ++ .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), ++ .of_match_table = fxos8700_of_match, ++ }, ++ .probe = fxos8700_i2c_probe, ++ .id_table = fxos8700_i2c_id, ++}; ++module_i2c_driver(fxos8700_i2c_driver); ++ ++MODULE_AUTHOR("Robert Jones "); ++MODULE_DESCRIPTION("FXOS8700 I2C driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c +new file mode 100644 +index 00000000..57e7bb6 +--- /dev/null ++++ b/drivers/iio/imu/fxos8700_spi.c +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * FXOS8700 - NXP IMU, SPI bits ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "fxos8700.h" ++ ++static int fxos8700_spi_probe(struct spi_device *spi) ++{ ++ struct regmap *regmap; ++ const struct spi_device_id *id = spi_get_device_id(spi); ++ ++ regmap = devm_regmap_init_spi(spi, &fxos8700_regmap_config); ++ if (IS_ERR(regmap)) { ++ dev_err(&spi->dev, "Failed to register spi regmap %d\n", ++ (int)PTR_ERR(regmap)); ++ return PTR_ERR(regmap); ++ } ++ ++ return fxos8700_core_probe(&spi->dev, regmap, id->name, true); ++} ++ ++static const struct spi_device_id fxos8700_spi_id[] = { ++ {"fxos8700", 0}, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, fxos8700_spi_id); ++ ++static const struct acpi_device_id fxos8700_acpi_match[] = { ++ {"FXOS8700", 0}, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match); ++ ++static const struct of_device_id fxos8700_of_match[] = { ++ { .compatible = "nxp,fxos8700" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, fxos8700_of_match); ++ ++static struct spi_driver fxos8700_spi_driver = { ++ .probe = fxos8700_spi_probe, ++ .id_table = fxos8700_spi_id, ++ .driver = { ++ .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), ++ .of_match_table = fxos8700_of_match, ++ .name = "fxos8700_spi", ++ }, ++}; ++module_spi_driver(fxos8700_spi_driver); ++ ++MODULE_AUTHOR("Robert Jones "); ++MODULE_DESCRIPTION("FXOS8700 SPI driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 +