From 30a97a3d54d2df74444d4da6c52589a688cdccb7 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 20 Sep 2017 22:16:30 +0100 Subject: iio: accel: fxos8700: add core driver for fxos8700 The NXP FXOS8700 is a accelerometer/magnetometer combo IC, this add support for the core functionalities using iio subsystem. It supports enable/disable each function (but not both, it is not supported by the chip), and reading axis values from the sysfs along with the respective scale. Signed-off-by: Rui Miguel Silva --- drivers/iio/accel/Kconfig | 10 + drivers/iio/accel/Makefile | 1 + drivers/iio/accel/fxos8700.h | 181 ++++++++++++ drivers/iio/accel/fxos8700_core.c | 560 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 752 insertions(+) create mode 100644 drivers/iio/accel/fxos8700.h create mode 100644 drivers/iio/accel/fxos8700_core.c diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 15de262015df..5648beb44250 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -134,6 +134,16 @@ config DMARD10 Choosing M will build the driver as a module. If so, the module will be called dmard10. +config FXOS8700_ACCEL_MAGN + tristate "NXP FXOS8700 Accelerometer / Magnetometer Driver" + select IIO_BUFFER + select REGMAP + help + Say yes here to build support for the FXOS8700 accelerometer / + magnetometer. + + This is a combo module with both accelerometer and magnetometer. + config HID_SENSOR_ACCEL_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 703e7c21f547..2ec7d765fa22 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_DA311) += da311.o obj-$(CONFIG_DMARD06) += dmard06.o obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_DMARD10) += dmard10.o +obj-$(CONFIG_FXOS8700_ACCEL_MAGN) += fxos8700_core.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o diff --git a/drivers/iio/accel/fxos8700.h b/drivers/iio/accel/fxos8700.h new file mode 100644 index 000000000000..2f008ae8c036 --- /dev/null +++ b/drivers/iio/accel/fxos8700.h @@ -0,0 +1,181 @@ +/* + * Driver for NXP FXOS8700 accelerometer/magnetometer header + * + * Copyright (C) 2017 Linaro Ltd. + * + */ + +#ifndef FXOS8700_H_ +#define FXOS8700_H_ + +#define FXOS8700_REG_STATUS 0x00 +#define FXOS8700_REG_OUT_X_MSB 0X01 +#define FXOS8700_REG_OUT_X_LSB 0x02 +#define FXOS8700_REG_OUT_Y_MSB 0x03 +#define FXOS8700_REG_OUT_Y_LSB 0x04 +#define FXOS8700_REG_OUT_Z_MSB 0x05 +#define FXOS8700_REG_OUT_Z_LSB 0x06 +#define FXOS8700_REG_F_SETUP 0x09 +#define FXOS8700_REG_TRIG_CFG 0x0a +#define FXOS8700_REG_SYSMOD 0x0B +#define FXOS8700_REG_INT_SOURCE 0x0c +#define FXOS8700_REG_WHO_AM_I 0x0d +#define FXOS8700_REG_XYZ_DATA_CFG 0x0e +#define FXOS8700_REG_HP_FILTER_CUTOFF 0x0f +#define FXOS8700_REG_PL_STATUS 0x10 +#define FXOS8700_REG_PL_CFG 0x11 +#define FXOS8700_REG_PL_COUNT 0x12 +#define FXOS8700_REG_PL_BF_ZCOMP 0x13 +#define FXOS8700_REG_PL_P_L_THS_REG 0x14 +#define FXOS8700_REG_FFMT_CFG 0x15 +#define FXOS8700_REG_FFMT_SRC 0x16 +#define FXOS8700_REG_FFMT_THS 0x17 +#define FXOS8700_REG_FFMT_COUNT 0x18 +#define FXOS8700_REG_TRANSIDENT1_CFG 0x1d +#define FXOS8700_REG_TRANSIDENT1_SRC 0x1e +#define FXOS8700_REG_TRANSIDENT1_THS 0x1f +#define FXOS8700_REG_TRANSIDENT1_COUNT 0x20 +#define FXOS8700_REG_PULSE_CFG 0x21 +#define FXOS8700_REG_PULSE_SRC 0x22 +#define FXOS8700_REG_PULSE_THSX 0x23 +#define FXOS8700_REG_PULSE_THSY 0x24 +#define FXOS8700_REG_PULSE_THSZ 0x25 +#define FXOS8700_REG_PULSE_TMLT 0x26 +#define FXOS8700_REG_PULSE_LTCY 0x27 +#define FXOS8700_REG_PULSE_WIND 0x28 +#define FXOS8700_REG_ATSLP_COUNT 0x29 +#define FXOS8700_REG_CTRL_REG1 0x2a +#define FXOS8700_REG_CTRL_REG2 0x2b +#define FXOS8700_REG_CTRL_REG3 0x2c +#define FXOS8700_REG_CTRL_REG4 0x2d +#define FXOS8700_REG_CTRL_REG5 0x2e +#define FXOS8700_REG_OFF_X 0x2f +#define FXOS8700_REG_OFF_Y 0x30 +#define FXOS8700_REG_OFF_Z 0x31 +#define FXOS8700_REG_M_DR_STATUS 0x32 +#define FXOS8700_REG_M_OUT_X_MSB 0x33 +#define FXOS8700_REG_M_OUT_X_LSB 0x34 +#define FXOS8700_REG_M_OUT_Y_MSB 0x35 +#define FXOS8700_REG_M_OUT_Y_LSB 0x36 +#define FXOS8700_REG_M_OUT_Z_MSB 0x37 +#define FXOS8700_REG_M_OUT_Z_LSB 0x38 +#define FXOS8700_REG_CMP_X_MSB 0x39 +#define FXOS8700_REG_CMP_X_LSB 0x3a +#define FXOS8700_REG_CMP_Y_MSB 0x3b +#define FXOS8700_REG_CMP_Y_LSB 0x3c +#define FXOS8700_REG_CMP_Z_MSB 0x3d +#define FXOS8700_REG_CMP_Z_LSB 0x3e +#define FXOS8700_REG_M_OFF_X_MSB 0x3f +#define FXOS8700_REG_M_OFF_X_LSB 0x40 +#define FXOS8700_REG_M_OFF_Y_MSB 0x41 +#define FXOS8700_REG_M_OFF_Y_LSB 0x42 +#define FXOS8700_REG_M_OFF_Z_MSB 0x43 +#define FXOS8700_REG_M_OFF_Z_LSB 0x44 +#define FXOS8700_REG_MAX_X_MSB 0x45 +#define FXOS8700_REG_MAX_X_LSB 0x46 +#define FXOS8700_REG_MAX_Y_MSB 0x47 +#define FXOS8700_REG_MAX_Y_LSB 0x48 +#define FXOS8700_REG_MAX_Z_MSB 0x49 +#define FXOS8700_REG_MAX_Z_LSB 0x4a +#define FXOS8700_REG_MIN_X_MSB 0x4b +#define FXOS8700_REG_MIN_X_LSB 0x4c +#define FXOS8700_REG_MIN_Y_MSB 0x4d +#define FXOS8700_REG_MIN_Y_LSB 0x4e +#define FXOS8700_REG_MIN_Z_MSB 0x4f +#define FXOS8700_REG_MIN_Z_LSB 0x50 +#define FXOS8700_REG_M_TEMP 0x51 +#define FXOS8700_REG_MAG_THS_CFG 0x52 +#define FXOS8700_REG_MAG_THS_SRC 0x53 +#define FXOS8700_REG_MAG_THS_THS_X1 0x54 +#define FXOS8700_REG_MAG_THS_THS_X0 0x55 +#define FXOS8700_REG_MAG_THS_THS_Y1 0x56 +#define FXOS8700_REG_MAG_THS_THS_Y0 0x57 +#define FXOS8700_REG_MAG_THS_THS_Z1 0x58 +#define FXOS8700_REG_MAG_THS_THS_Z0 0x59 +#define FXOS8700_REG_MAG_THS_CUNT 0x5a +#define FXOS8700_REG_M_CTRL_REG1 0x5b +#define FXOS8700_REG_M_CTRL_REG2 0x5c +#define FXOS8700_REG_M_CTRL_REG3 0x5d +#define FXOS8700_REG_M_INT_SOURCE 0x5e +#define FXOS8700_REG_G_VECM_CFG 0x5f +#define FXOS8700_REG_G_VECM_THS_MSB 0x60 +#define FXOS8700_REG_G_VECM_THS_LSB 0x61 +#define FXOS8700_REG_G_VECM_CNT 0x62 +#define FXOS8700_REG_G_VECM_INITX_MSB 0x63 +#define FXOS8700_REG_G_VECM_INITX_LSB 0x64 +#define FXOS8700_REG_G_VECM_INITY_MSB 0x65 +#define FXOS8700_REG_G_VECM_INITY_LSB 0x66 +#define FXOS8700_REG_G_VECM_INITZ_MSB 0x67 +#define FXOS8700_REG_G_VECM_INITZ_LSB 0x68 +#define FXOS8700_REG_M_VECM_CFG 0x69 +#define FXOS8700_REG_M_VECM_THS_MSB 0x6a +#define FXOS8700_REG_M_VECM_THS_LSB 0x6b +#define FXOS8700_REG_M_VECM_CNT 0x6d +#define FXOS8700_REG_M_VECM_INITX_MSB 0x6d +#define FXOS8700_REG_M_VECM_INITX_LSB 0x6e +#define FXOS8700_REG_M_VECM_INITY_MSB 0x6f +#define FXOS8700_REG_M_VECM_INITY_LSB 0x70 +#define FXOS8700_REG_M_VECM_INITZ_MSB 0x71 +#define FXOS8700_REG_M_VECM_INITZ_LSB 0x72 +#define FXOS8700_REG_G_FFMT_THS_X1 0x73 +#define FXOS8700_REG_G_FFMT_THS_X0 0x74 +#define FXOS8700_REG_G_FFMT_THS_Y1 0x75 +#define FXOS8700_REG_G_FFMT_THS_Y0 0x76 +#define FXOS8700_REG_G_FFMT_THS_Z1 0x77 +#define FXOS8700_REG_G_FFMT_THS_Z0 0x78 +#define FXOS8700_REG_G_TRAN_INIT_MSB 0x79 +#define FXOS8700_REG_G_TRAN_INIT_LSB_X 0x7a +#define FXOS8700_REG_G_TRAN_INIT_LSB_Y 0x7b +#define FXOS8700_REG_G_TRAN_INIT_LSB_Z 0x7d +#define FXOS8700_REG_TM_NVM_LOCK 0x7e +#define FXOS8700_REG_NVM_DATA0_35 0x80 +#define FXOS8700_REG_NVM_DATA_BNK3 0xa4 +#define FXOS8700_REG_NVM_DATA_BNK2 0xa5 +#define FXOS8700_REG_NVM_DATA_BNK1 0xa6 +#define FXOS8700_REG_NVM_DATA_BNK0 0xa7 + +enum fxos8700_fields { + F_DR_STATUS, + F_OUT_X_MSB, + F_OUT_X_LSB, + F_OUT_Y_MSB, + F_OUT_Y_LSB, + F_OUT_Z_MSB, + F_OUT_Z_LSB, + /* WHO_AM_I */ + F_WHO_AM_I, + /* CTRL_REG1 */ + F_ASLP_RATE, F_DR, F_INOISE, F_F_READ, F_ACTIVE, + /* CTRL_M_REG1 */ + F_M_ACAL, F_M_RST, F_M_OST, F_M_OS, F_M_HMS, + /* MAX FIELDS */ + F_MAX_FIELDS, +}; + +static const struct reg_field fxos8700_reg_fields[] = { + [F_DR_STATUS] = REG_FIELD(FXOS8700_REG_STATUS, 0, 7), + [F_OUT_X_MSB] = REG_FIELD(FXOS8700_REG_OUT_X_MSB, 0, 7), + [F_OUT_X_LSB] = REG_FIELD(FXOS8700_REG_OUT_X_LSB, 0, 7), + [F_OUT_Y_MSB] = REG_FIELD(FXOS8700_REG_OUT_Y_MSB, 0, 7), + [F_OUT_Y_LSB] = REG_FIELD(FXOS8700_REG_OUT_Y_LSB, 0, 7), + [F_OUT_Z_MSB] = REG_FIELD(FXOS8700_REG_OUT_Z_MSB, 0, 7), + [F_OUT_Z_LSB] = REG_FIELD(FXOS8700_REG_OUT_Z_LSB, 0, 7), + [F_WHO_AM_I] = REG_FIELD(FXOS8700_REG_WHO_AM_I, 0, 7), + [F_ASLP_RATE] = REG_FIELD(FXOS8700_REG_CTRL_REG1, 6, 7), + [F_DR] = REG_FIELD(FXOS8700_REG_CTRL_REG1, 3, 5), + [F_INOISE] = REG_FIELD(FXOS8700_REG_CTRL_REG1, 2, 2), + [F_F_READ] = REG_FIELD(FXOS8700_REG_CTRL_REG1, 1, 1), + [F_ACTIVE] = REG_FIELD(FXOS8700_REG_CTRL_REG1, 0, 0), + [F_M_ACAL] = REG_FIELD(FXOS8700_REG_M_CTRL_REG1, 7, 7), + [F_M_RST] = REG_FIELD(FXOS8700_REG_M_CTRL_REG1, 6, 6), + [F_M_OST] = REG_FIELD(FXOS8700_REG_M_CTRL_REG1, 5, 5), + [F_M_OS] = REG_FIELD(FXOS8700_REG_M_CTRL_REG1, 2, 4), + [F_M_HMS] = REG_FIELD(FXOS8700_REG_M_CTRL_REG1, 0, 1), +}; + +extern const struct dev_pm_ops fxos8700_pm_ops; + +int fxos8700_core_probe(struct device *dev, struct regmap *regmap, + const char *name); +void fxos8700_core_remove(struct device *dev); +#endif diff --git a/drivers/iio/accel/fxos8700_core.c b/drivers/iio/accel/fxos8700_core.c new file mode 100644 index 000000000000..6c2103c67f6c --- /dev/null +++ b/drivers/iio/accel/fxos8700_core.c @@ -0,0 +1,560 @@ +/* + * Driver for NXP FXOS8700 Accelerometer and Magnetometer - Core + * + * Copyright (C) 2017 Linaro Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fxos8700.h" + +#define FXOS8700_CHIP_ID 0xC4 +#define FXOS8700_PRE_CHIP_ID 0xC7 + +#define FXOS8700_MODE_STANDBY 0x00 +#define FXOS8700_MODE_ACTIVE 0x01 + +#define FXOS8700_ACCEL_ON 0x00 +#define FXOS8700_MAGN_ON 0x01 +#define FXOS8700_HYBRID_ON 0x03 + +struct fxos8700_data { + u8 chip_id; + u8 mode; + u8 prev_mode; + bool accel_enabled; + bool magn_enabled; + struct regmap *regmap; + struct regmap_field *regmap_fields[F_MAX_FIELDS]; + struct mutex mutex; /* protect data */ +}; + +static int fxos8700_pm_get(struct fxos8700_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + + return ret; +} + +static int fxos8700_pm_put(struct fxos8700_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int fxos8700_mode_get(struct fxos8700_data *data) +{ + unsigned int mode; + int ret; + + ret = regmap_field_read(data->regmap_fields[F_ACTIVE], &mode); + if (ret < 0) + return ret; + + return mode; +} + +static int fxos8700_mode_set(struct fxos8700_data *data, u8 mode) +{ + int ret; + + if (mode > FXOS8700_MODE_ACTIVE) + return -EINVAL; + + if (mode == FXOS8700_MODE_ACTIVE) + ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 1); + else + ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 0); + if (ret < 0) + return ret; + + data->prev_mode = data->mode; + data->mode = mode; + + return ret; +} + +static int fxos8700_pre_write(struct fxos8700_data *data) +{ + int actual_mode; + int ret; + + actual_mode = fxos8700_mode_get(data); + + if (actual_mode < 0) + return actual_mode; + + ret = fxos8700_mode_set(data, FXOS8700_MODE_STANDBY); + if (ret < 0) + return ret; + + /* going from active to standby varies from 0 to 300ms */ + msleep_interruptible(300); + + return ret; +} + +static int fxos8700_post_write(struct fxos8700_data *data) +{ + return fxos8700_mode_set(data, data->prev_mode); +} + +static int fxos8700_axis_get(struct fxos8700_data *data, unsigned long addr, + int type, int *val) +{ + struct device *dev = regmap_get_device(data->regmap); + __be16 axis_be; + unsigned int reg; + int bits; + int ret_pm; + int ret; + + mutex_lock(&data->mutex); + ret = fxos8700_pm_get(data); + if (ret < 0) + goto data_unlock; + + reg = addr; + + /* For magnetometer we need to first read OUT_X_MSB to update the other + * axis, not needed of course if we are getting OUT_X + */ + if (type == IIO_MAGN && reg != FXOS8700_REG_OUT_X_MSB) { + ret = regmap_bulk_read(data->regmap, FXOS8700_REG_OUT_X_MSB, + &axis_be, sizeof(axis_be)); + if (ret < 0) { + dev_err(dev, "failed to read axis: %d\n", ret); + goto pm_put; + } + } + + ret = regmap_bulk_read(data->regmap, reg, &axis_be, sizeof(axis_be)); + if (ret < 0) { + dev_err(dev, "failed to read axis: %d\n", ret); + goto pm_put; + } + + /* For accelerometer only use a complement by 2' 14 bits */ + if (type == IIO_ACCEL) { + bits = 14; + axis_be = (axis_be >> 2); + } else { + bits = 16; + } + + *val = sign_extend32(be16_to_cpu(axis_be), bits); + + ret = IIO_VAL_INT; + +pm_put: + ret_pm = fxos8700_pm_put(data); + if (ret_pm < 0) + ret = ret_pm; + +data_unlock: + mutex_unlock(&data->mutex); + + return ret; +} + +static int fxos8700_hms_get(struct fxos8700_data *data, int type, int *val) +{ + unsigned int hms; + int ret_pm; + int ret; + + mutex_lock(&data->mutex); + ret = fxos8700_pm_get(data); + if (ret < 0) + goto data_unlock; + + ret = regmap_field_read(data->regmap_fields[F_M_HMS], &hms); + if (ret < 0) + goto pm_put; + + *val = 0; + + if (type == IIO_MAGN) { + if ((hms == FXOS8700_MAGN_ON) || (hms == FXOS8700_HYBRID_ON)) + *val = 1; + } else { + if ((hms == FXOS8700_ACCEL_ON) || (hms == FXOS8700_HYBRID_ON)) + *val = 1; + } + + ret = IIO_VAL_INT; + +pm_put: + ret_pm = fxos8700_pm_put(data); + if (ret_pm < 0) + ret = ret_pm; + +data_unlock: + mutex_unlock(&data->mutex); + + return ret; +} + +static int fxos8700_hms_set(struct fxos8700_data *data, u8 hms) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + mutex_lock(&data->mutex); + ret = fxos8700_pre_write(data); + if (ret < 0) + goto post_write; + + ret = regmap_field_write(data->regmap_fields[F_M_HMS], hms); + if (ret < 0) + goto post_write; + + if (hms == FXOS8700_HYBRID_ON) { + data->magn_enabled = true; + data->accel_enabled = true; + } else if (hms == FXOS8700_ACCEL_ON) { + data->magn_enabled = false; + data->accel_enabled = true; + } else if (hms == FXOS8700_MAGN_ON) { + data->accel_enabled = false; + data->magn_enabled = true; + } + +post_write: + fxos8700_post_write(data); + + mutex_unlock(&data->mutex); + + dev_info(dev, "accelerometer: %s magnetometer: %s\n", + data->accel_enabled ? "on" : "off", + data->magn_enabled ? "on" : "off"); + + return ret; +} + +static int fxos8700_osr_set(struct fxos8700_data *data, u8 osr) +{ + int ret; + + mutex_lock(&data->mutex); + ret = fxos8700_pre_write(data); + if (ret < 0) + goto post_write; + + ret = regmap_field_write(data->regmap_fields[F_M_OS], osr); + if (ret < 0) + goto post_write; + +post_write: + fxos8700_post_write(data); + + mutex_unlock(&data->mutex); + + return ret; +} + +static int fxos8700_read_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_RAW: + switch (chan->type) { + case IIO_MAGN: + case IIO_ACCEL: + return fxos8700_axis_get(data, chan->address, + chan->type, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_ENABLE: + return fxos8700_hms_get(data, chan->type, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + *val = 0; + *val2 = 1; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + *val2 = 244; /* at +/-2g is 244mg/LSB */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + 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); + struct device *dev = regmap_get_device(data->regmap); + u8 hms; + + switch (mask) { + case IIO_CHAN_INFO_ENABLE: + if (chan->type == IIO_MAGN) { + if (data->magn_enabled == !!val) + return 0; + + data->magn_enabled = !!val; + } + + if (chan->type == IIO_ACCEL) { + if (data->accel_enabled == !!val) + return 0; + + data->accel_enabled = !!val; + } + + if (data->magn_enabled && data->accel_enabled) { + hms = FXOS8700_HYBRID_ON; + } else if (data->magn_enabled) { + hms = FXOS8700_MAGN_ON; + } else if (data->accel_enabled) { + hms = FXOS8700_ACCEL_ON; + } else { + dev_err(dev, "both accel and magn can not be off at the same time\n"); + return -EINVAL; + } + + return fxos8700_hms_set(data, hms); + default: + return -EINVAL; + } +} + +#define FXOS8700_ACCEL_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .address = FXOS8700_REG_OUT_##_axis##_MSB, \ + .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_ENABLE), \ +} + +#define FXOS8700_MAGN_CHANNEL(_axis) { \ + .type = IIO_MAGN, \ + .address = FXOS8700_REG_M_OUT_##_axis##_MSB, \ + .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_ENABLE), \ +} + +static const struct iio_chan_spec fxos8700_channels[] = { + FXOS8700_ACCEL_CHANNEL(X), + FXOS8700_ACCEL_CHANNEL(Y), + FXOS8700_ACCEL_CHANNEL(Z), + FXOS8700_MAGN_CHANNEL(X), + FXOS8700_MAGN_CHANNEL(Y), + FXOS8700_MAGN_CHANNEL(Z), +}; + +static const struct iio_info fxos8700_info = { + .driver_module = THIS_MODULE, + .read_raw = &fxos8700_read_raw, + .write_raw = &fxos8700_write_raw, +}; + +static int fxos8700_chip_init(struct fxos8700_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int chip_id; + int ret; + + ret = regmap_field_read(data->regmap_fields[F_WHO_AM_I], &chip_id); + if (ret < 0) + return ret; + + if (chip_id != FXOS8700_CHIP_ID && chip_id != FXOS8700_PRE_CHIP_ID) { + dev_err(dev, "chip id %d is not supported\n", chip_id); + return -EINVAL; + } + + data->chip_id = chip_id; + + ret = fxos8700_mode_set(data, FXOS8700_MODE_STANDBY); + if (ret < 0) + return ret; + + /* Hybrid mode set - accel on and magnet on */ + ret = fxos8700_hms_set(data, FXOS8700_HYBRID_ON); + if (ret < 0) { + dev_err(dev, "failed to set hybrid mode: %d\n", ret); + return ret; + } + + /* Set oversampling to 8x for ODR 200Hz */ + ret = fxos8700_osr_set(data, 0x07); + if (ret < 0) + dev_err(dev, "failed to set OSR: %d\n", ret); + + return ret; +} + +int fxos8700_core_probe(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct fxos8700_data *data; + struct iio_dev *indio_dev; + struct regmap_field *f; + int ret; + int i; + + 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; + + for (i = 0; i < F_MAX_FIELDS; i++) { + f = devm_regmap_field_alloc(dev, data->regmap, + fxos8700_reg_fields[i]); + if (IS_ERR(f)) { + dev_err(dev, "failed to alloc regmap field %d: %ld\n", + i, PTR_ERR(f)); + return PTR_ERR(f); + } + data->regmap_fields[i] = f; + } + + mutex_init(&data->mutex); + + ret = fxos8700_chip_init(data); + if (ret < 0) + return ret; + + indio_dev->dev.parent = dev; + indio_dev->channels = fxos8700_channels; + indio_dev->num_channels = ARRAY_SIZE(fxos8700_channels); + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &fxos8700_info; + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); + pm_runtime_use_autosuspend(dev); + + fxos8700_mode_set(data, FXOS8700_MODE_ACTIVE); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "unable to register iio device: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(fxos8700_core_probe); + +void fxos8700_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxos8700_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + fxos8700_mode_set(data, FXOS8700_MODE_STANDBY); +} +EXPORT_SYMBOL_GPL(fxos8700_core_remove); + +#ifdef CONFIG_PM_SLEEP +static int fxos8700_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxos8700_data *data = iio_priv(indio_dev); + + fxos8700_mode_set(data, FXOS8700_MODE_STANDBY); + + return 0; +} + +static int fxos8700_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxos8700_data *data = iio_priv(indio_dev); + + fxos8700_mode_set(data, data->prev_mode); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int fxos8700_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxos8700_data *data = iio_priv(indio_dev); + int ret; + + ret = fxos8700_mode_set(data, FXOS8700_MODE_STANDBY); + if (ret < 0) + return -EAGAIN; + + return 0; +} + +static int fxos8700_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxos8700_data *data = iio_priv(indio_dev); + int ret; + + ret = fxos8700_mode_set(data, FXOS8700_MODE_ACTIVE); + if (ret < 0) + return -EAGAIN; + + return 0; +} +#endif + +const struct dev_pm_ops fxos8700_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fxos8700_suspend, fxos8700_resume) + SET_RUNTIME_PM_OPS(fxos8700_runtime_suspend, fxos8700_runtime_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(fxos8700_pm_ops); + +MODULE_AUTHOR("Rui Miguel Silva "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("FXOS8700 Accel/Magn driver"); -- cgit v1.2.3