| From 84e5ddd5c46ea3bf0cad670da32028994cad5936 Mon Sep 17 00:00:00 2001 |
| From: Robert Jones <rjones@gateworks.com> |
| 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 <rjones@gateworks.com> |
| Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> |
| --- |
| 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 |
| |
| --- 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 |
| --- a/drivers/iio/imu/Makefile |
| +++ b/drivers/iio/imu/Makefile |
| @@ -14,6 +14,11 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) + |
| 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 |
| --- /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_ */ |
| --- /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 <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] ____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 <rjones@gateworks.com>"); |
| +MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver"); |
| +MODULE_LICENSE("GPL v2"); |
| --- /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 <linux/acpi.h> |
| +#include <linux/i2c.h> |
| +#include <linux/module.h> |
| +#include <linux/mod_devicetable.h> |
| +#include <linux/regmap.h> |
| + |
| +#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 <rjones@gateworks.com>"); |
| +MODULE_DESCRIPTION("FXOS8700 I2C driver"); |
| +MODULE_LICENSE("GPL v2"); |
| --- /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 <linux/acpi.h> |
| +#include <linux/module.h> |
| +#include <linux/mod_devicetable.h> |
| +#include <linux/regmap.h> |
| +#include <linux/spi/spi.h> |
| + |
| +#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 <rjones@gateworks.com>"); |
| +MODULE_DESCRIPTION("FXOS8700 SPI driver"); |
| +MODULE_LICENSE("GPL v2"); |