Re: [PATCH v3 2/2] iio: imu: Add support for the FXOS8700 IMU

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 10 Oct 2019 10:56:48 -0700
Robert Jones <rjones@xxxxxxxxxxxxx> wrote:

> 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@xxxxxxxxxxxxx>
Mostly looks good.  A few remaining bits inline.

The endian types seems to have gotten very confused!

Jonathan

> ---
>  drivers/iio/imu/Kconfig         |  27 +++
>  drivers/iio/imu/Makefile        |   5 +
>  drivers/iio/imu/fxos8700.h      | 185 +++++++++++++++
>  drivers/iio/imu/fxos8700_core.c | 499 ++++++++++++++++++++++++++++++++++++++++
>  drivers/iio/imu/fxos8700_i2c.c  |  71 ++++++
>  drivers/iio/imu/fxos8700_spi.c  |  59 +++++
>  6 files changed, 846 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 0000000..92499a2
> --- /dev/null
> +++ b/drivers/iio/imu/fxos8700.h
> @@ -0,0 +1,185 @@
> +/* 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);
> +
> +/* Register Definitions */

If register definitions are only used in one c file, move them there
rather than exposing the in the header.

> +#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_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 SENSOR_IOCTL_BASE           'S'
> +#define SENSOR_GET_MODEL_NAME       _IOR(SENSOR_IOCTL_BASE, 0, char *)
> +#define SENSOR_GET_POWER_STATUS     _IOR(SENSOR_IOCTL_BASE, 2, int)
> +#define SENSOR_SET_POWER_STATUS     _IOR(SENSOR_IOCTL_BASE, 3, int)
> +#define SENSOR_GET_DELAY_TIME       _IOR(SENSOR_IOCTL_BASE, 4, int)
> +#define SENSOR_SET_DELAY_TIME       _IOR(SENSOR_IOCTL_BASE, 5, int)
> +#define SENSOR_GET_RAW_DATA         _IOR(SENSOR_IOCTL_BASE, 6, short[3])

What are these doing here?

> +
> +#define FXOS8700_I2C_ADDR           0x1E
> +#define FXOS8700_DEVICE_ID          0xC7
> +#define FXOS8700_PRE_DEVICE_ID      0xC4
> +#define FXOS8700_DATA_BUF_SIZE      6
> +#define FXOS8700_DELAY_DEFAULT      200 /* msecs */
> +#define FXOS8700_POSITION_DEFAULT   1   /* msecs */
> +
> +#define FXOS8700_TYPE_ACC           0x00
> +#define FXOS8700_TYPE_MAG           0x01
> +#define FXOS8700_STANDBY            0x00
> +#define FXOS8700_ACTIVE             0x01
> +#define FXOS8700_ACTIVE_MIN_USLEEP  4000 /* from table 6 in datasheet */
> +
> +#define ABS_STATUS                  ABS_WHEEL
> +#define FXOS8700_DRIVER             "fxos8700"
> +
> +#define ABSMAX_ACC_VAL              0x01FF
> +#define ABSMIN_ACC_VAL              -(ABSMAX_ACC_VAL)
> +#define FXOS8700_POLL_INTERVAL      400
> +#define FXOS8700_POLL_MAX           800
> +#define FXOS8700_POLL_MIN           100
> +#define FXOS8700_CTRL_ODR_MSK       0x38
> +#define FXOS8700_CTRL_ODR_MAX       0x00
> +#define FXOS8700_CTRL_ODR_MIN       (0x03 << 3)
> +
> +enum {
> +	MODE_2G = 0,
> +	MODE_4G,
> +	MODE_8G,
> +};
> +
> +#endif  /* FXOS8700_H_ */
> diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c
> new file mode 100644
> index 0000000..ea2716b
> --- /dev/null
> +++ b/drivers/iio/imu/fxos8700_core.c
> @@ -0,0 +1,499 @@
> +// 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"
> +
> +struct fxos8700_data {
> +	struct regmap *regmap;
> +	struct iio_trigger *trig;
> +	u8 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),			\
> +}
> +
> +/* 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;
> +	__le16 sample = 0;
> +	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;
> +
> +	/* Composite individual axis MSB/LSB registers */
> +	sample = ((u16 *)data->buf)[reg];

Firstly cast to le16, then secondly don't then do a be16 to
cpu conversion?  Which one is it le16 or be16?

> +
> +	/* Convert to native endianness */
> +	*val = sign_extend32(be16_to_cpu(sample), 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);
> +		break;
> +	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) */
> +	ret = regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +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@xxxxxxxxxxxxx>");
> +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 0000000..73a5f91
> --- /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/of.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         = of_match_ptr(fxos8700_of_match),
> +	},
> +	.probe          = fxos8700_i2c_probe,
> +	.id_table       = fxos8700_i2c_id,
> +};
> +module_i2c_driver(fxos8700_i2c_driver);
> +
> +MODULE_AUTHOR("Robert Jones <rjones@xxxxxxxxxxxxx>");
> +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 0000000..412ff08
> --- /dev/null
> +++ b/drivers/iio/imu/fxos8700_spi.c
> @@ -0,0 +1,59 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FXOS8700 - NXP IMU, I2C bits
> + *
nitpick :) Trailing blank line here doesn't add anything.

> + */
> +#include <linux/acpi.h>
> +#include <linux/module.h>
> +#include <linux/of.h>

If we are spinning again, another thread I read today pointed out that
we don't actually need the of header in these files, but rather just
mod_devicetable.h so it would be better to include that.

> +#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@xxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("FXOS8700 SPI driver");
> +MODULE_LICENSE("GPL v2");





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux