* [PATCH v3 0/2] iio:gyro:bmg160 Gyro Sensor driver @ 2014-08-05 21:57 Srinivas Pandruvada 2014-08-05 21:57 ` [PATCH v3 1/2] " Srinivas Pandruvada 2014-08-05 21:57 ` [PATCH v3 2/2] iio:gyro:bmg160 documentation Srinivas Pandruvada 0 siblings, 2 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2014-08-05 21:57 UTC (permalink / raw) To: jic23; +Cc: linux-iio, Srinivas Pandruvada v3 - Suport Only Runtime PM no fallback method. Driver will still work but chip will be in power on mode. - Add comment for enable disable trigger/event sequence, how they work in reverse order using runtime pm calls v2: - Add device id in any-motion trigger - Removed string comparison for trigger name to identify trigger source - Prevent disabling any motion interrupts when both any motion trigger and events are on, but any one of them is disabled via sysfs - Switch on chan->type - Created new function to raw read axis - Explanation why need to power on for changing sampling freq and scale v1: - Runtime PM: Removed module parameter for delay configuration Use RTPM autosuspend delay feature. This allows sysfs configuration of autosuspend time. - Trigger As suggested, there are two triggers: - data_ready : Trigger which will allow all gyro periodic sampled data to be received by user space - any-motion: When used, will configure the chipset so that data is pushed only when there is any motion, allowing system to enter deeper idle power states - Events ROC type events will send direction instead of either. Also both events and data triggers can be enabled together. - Addition of unbuffered TEMP read interface - Misc error message corrections as suggested by Peter Meerwald TBD: Add sysfs attribute to trigger to configure thresholds. Currently the triggers don't have feature to add sysfs attributes unlike IIO device. But this has to be a RFC patch on top. v0: Base version for review Srinivas Pandruvada (2): iio:gyro:bmg160 Gyro Sensor driver iio:gyro:bmg160 documentation .../ABI/testing/sysfs-bus-iio-gyro-bmg160 | 7 + drivers/iio/gyro/Kconfig | 11 + drivers/iio/gyro/Makefile | 1 + drivers/iio/gyro/bmg160.c | 1211 ++++++++++++++++++++ 4 files changed, 1230 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 create mode 100644 drivers/iio/gyro/bmg160.c -- 1.7.11.7 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-05 21:57 [PATCH v3 0/2] iio:gyro:bmg160 Gyro Sensor driver Srinivas Pandruvada @ 2014-08-05 21:57 ` Srinivas Pandruvada 2014-08-07 10:39 ` Jonathan Cameron 2014-08-05 21:57 ` [PATCH v3 2/2] iio:gyro:bmg160 documentation Srinivas Pandruvada 1 sibling, 1 reply; 8+ messages in thread From: Srinivas Pandruvada @ 2014-08-05 21:57 UTC (permalink / raw) To: jic23; +Cc: linux-iio, Srinivas Pandruvada This change implements support for BMG160 Gyro sensor. Although chip has several advanced features, this change implements minimum set required for using gyro sensor. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> --- drivers/iio/gyro/Kconfig | 11 + drivers/iio/gyro/Makefile | 1 + drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1223 insertions(+) create mode 100644 drivers/iio/gyro/bmg160.c diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index ac2d69e..d630ae9 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -50,6 +50,17 @@ config ADXRS450 This driver can also be built as a module. If so, the module will be called adxrs450. +config BMG160 + tristate "BOSCH BMG160 Gyro Sensor" + depends on I2C + select IIO_TRIGGERED_BUFFER if IIO_BUFFER + help + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor + driver. + + This driver can also be built as a module. If so, the module + will be called bmg160. + config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 2f2752a..36a3877 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o obj-$(CONFIG_ADIS16136) += adis16136.o obj-$(CONFIG_ADIS16260) += adis16260.o obj-$(CONFIG_ADXRS450) += adxrs450.o +obj-$(CONFIG_BMG160) += bmg160.o obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c new file mode 100644 index 0000000..80f92a6 --- /dev/null +++ b/drivers/iio/gyro/bmg160.c @@ -0,0 +1,1211 @@ +/* + * BMG160 Gyro Sensor driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define BMG160_DRV_NAME "bmg160" +#define BMG160_IRQ_NAME "bmg160_event" +#define BMG160_GPIO_NAME "gpio_int" + +#define BMG160_REG_CHIP_ID 0x00 +#define BMG160_CHIP_ID_VAL 0x0F + +#define BMG160_REG_PMU_LPW 0x11 +#define BMG160_MODE_NORMAL 0x00 +#define BMG160_MODE_DEEP_SUSPEND 0x20 +#define BMG160_MODE_SUSPEND 0x80 + +#define BMG160_REG_RANGE 0x0F + +#define BMG160_RANGE_2000DPS 0 +#define BMG160_RANGE_1000DPS 1 +#define BMG160_RANGE_500DPS 2 +#define BMG160_RANGE_250DPS 3 +#define BMG160_RANGE_125DPS 4 + +#define BMG160_REG_PMU_BW 0x10 +#define BMG160_NO_FILTER 0 +#define BMG160_DEF_BW 100 + +#define BMG160_REG_INT_MAP_0 0x17 +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) + +#define BMG160_REG_INT_MAP_1 0x18 +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) + +#define BMG160_REG_INT_RST_LATCH 0x21 +#define BMG160_INT_MODE_LATCH_RESET 0x80 +#define BMG160_INT_MODE_LATCH_INT 0x0F +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 + +#define BMG160_REG_INT_EN_0 0x15 +#define BMG160_DATA_ENABLE_INT BIT(7) + +#define BMG160_REG_XOUT_L 0x02 +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) + +#define BMG160_REG_SLOPE_THRES 0x1B +#define BMG160_SLOPE_THRES_MASK 0x0F + +#define BMG160_REG_MOTION_INTR 0x1C +#define BMG160_INT_MOTION_X BIT(0) +#define BMG160_INT_MOTION_Y BIT(1) +#define BMG160_INT_MOTION_Z BIT(2) +#define BMG160_ANY_DUR_MASK 0x30 +#define BMG160_ANY_DUR_SHIFT 4 + +#define BMG160_REG_INT_STATUS_2 0x0B +#define BMG160_ANY_MOTION_MASK 0x07 + +#define BMG160_REG_TEMP 0x08 +#define BMG160_TEMP_CENTER_VAL 23 + +#define BMG160_MAX_STARTUP_TIME_MS 80 + +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 + +struct bmg160_data { + struct i2c_client *client; + struct iio_trigger *dready_trig; + struct iio_trigger *motion_trig; + struct mutex mutex; + s16 buffer[8]; + u8 bw_bits; + u32 dps_range; + int ev_enable_state; + int slope_thres; + bool dready_trigger_on; + bool motion_trigger_on; + int64_t timestamp; +}; + +enum bmg160_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static const struct { + int val; + int bw_bits; +} bmg160_samp_freq_table[] = { {100, 0x07}, + {200, 0x06}, + {400, 0x03}, + {1000, 0x02}, + {2000, 0x01} }; + +static const struct { + int scale; + int dps_range; +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, + { 532, BMG160_RANGE_1000DPS}, + { 266, BMG160_RANGE_500DPS}, + { 133, BMG160_RANGE_250DPS}, + { 66, BMG160_RANGE_125DPS} }; + +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_PMU_LPW, mode); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); + return ret; + } + + return 0; +} + +static int bmg160_convert_freq_to_bit(int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].val == val) + return bmg160_samp_freq_table[i].bw_bits; + } + + return -EINVAL; +} + +static int bmg160_set_bw(struct bmg160_data *data, int val) +{ + int ret; + int bw_bits; + + bw_bits = bmg160_convert_freq_to_bit(val); + if (bw_bits < 0) + return bw_bits; + + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, + bw_bits); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); + return ret; + } + + data->bw_bits = bw_bits; + + return 0; +} + +static int bmg160_chip_init(struct bmg160_data *data) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); + return ret; + } + + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); + if (ret != BMG160_CHIP_ID_VAL) { + dev_err(&data->client->dev, "invalid chip %x\n", ret); + return -ENODEV; + } + + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); + if (ret < 0) + return ret; + + /* Wait upto 500 ms to be ready after changing mode */ + usleep_range(500, 1000); + + /* Set Bandwidth */ + ret = bmg160_set_bw(data, BMG160_DEF_BW); + if (ret < 0) + return ret; + + /* Set Default Range */ + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_RANGE, + BMG160_RANGE_500DPS); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_range\n"); + return ret; + } + data->dps_range = BMG160_RANGE_500DPS; + + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); + return ret; + } + data->slope_thres = ret; + + /* Set default interrupt mode */ + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_motion_intr\n"); + return ret; + } + + return 0; +} + +static int bmg160_set_power_state(struct bmg160_data *data, bool on) +{ + int ret; + + if (on) + ret = pm_runtime_get_sync(&data->client->dev); + else { + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); + } + + if (ret < 0) { + dev_err(&data->client->dev, + "Failed: bmg160_set_power_state for %d\n", on); + return ret; + } + + return 0; +} + +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, + bool status) +{ + int ret; + + /* Enable/Disable INT_MAP0 mapping */ + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); + return ret; + } + if (status) + ret |= BMG160_INT_MAP_0_BIT_ANY; + else + ret &= ~BMG160_INT_MAP_0_BIT_ANY; + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_MAP_0, + ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); + return ret; + } + + /* Enable/Disable slope interrupts */ + if (status) { + /* Update slope thres */ + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_SLOPE_THRES, + data->slope_thres); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_slope_thres\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_MOTION_INTR, + BMG160_INT_MOTION_X | + BMG160_INT_MOTION_Y | + BMG160_INT_MOTION_Z); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_motion_intr\n"); + return ret; + } + + /* + * New data interrupt is always non-latched, + * which will have higher priority, so no need + * to set latched mode, we will be flooded anyway with INTR + */ + if (!data->dready_trigger_on) { + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + } + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); + + } else + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_0, + 0); + + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); + return ret; + } + + return 0; +} + +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, + bool status) +{ + int ret; + + /* Enable/Disable INT_MAP1 mapping */ + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); + return ret; + } + + if (status) + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; + else + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_MAP_1, + ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); + return ret; + } + + if (status) { + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_NON_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); + + } else { + /* Restore interrupt mode */ + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_0, + 0); + } + + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); + return ret; + } + + return 0; +} + +static int bmg160_get_bw(struct bmg160_data *data, int *val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { + *val = bmg160_samp_freq_table[i].val; + return IIO_VAL_INT; + } + } + + return -EINVAL; +} + +static int bmg160_set_scale(struct bmg160_data *data, int val) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { + if (bmg160_scale_table[i].scale == val) { + ret = i2c_smbus_write_byte_data( + data->client, + BMG160_REG_RANGE, + bmg160_scale_table[i].dps_range); + if (ret < 0) { + dev_err(&data->client->dev, + "Error writing reg_range\n"); + return ret; + } + data->dps_range = bmg160_scale_table[i].dps_range; + return 0; + } + } + + return -EINVAL; +} + +static int bmg160_get_temp(struct bmg160_data *data, int *val) +{ + int ret; + + mutex_lock(&data->mutex); + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_temp\n"); + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + + *val = sign_extend32(ret, 7); + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + + return IIO_VAL_INT; +} + +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) +{ + int ret; + + mutex_lock(&data->mutex); + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading axis %d\n", axis); + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + + *val = sign_extend32(ret, 15); + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + + return IIO_VAL_INT; +} + +static int bmg160_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + return bmg160_get_temp(data, val); + case IIO_ANGL_VEL: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + else + return bmg160_get_axis(data, chan->scan_index, + val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + *val = BMG160_TEMP_CENTER_VAL; + return IIO_VAL_INT; + } else + return -EINVAL; + case IIO_CHAN_INFO_SCALE: + *val = 0; + switch (chan->type) { + case IIO_TEMP: + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ANGL_VEL: + { + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { + if (bmg160_scale_table[i].dps_range == + data->dps_range) { + *val2 = bmg160_scale_table[i].scale; + return IIO_VAL_INT_PLUS_MICRO; + } + } + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val2 = 0; + mutex_lock(&data->mutex); + ret = bmg160_get_bw(data, val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int bmg160_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->mutex); + /* + * Section 4.2 of spec + * In suspend mode, the only supported operations are reading + * registers as well as writing to the (0x14) softreset + * register. Since we will be in suspend mode by default, change + * mode to power on for other writes. + */ + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_bw(data, val); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SCALE: + if (val) + return -EINVAL; + + mutex_lock(&data->mutex); + /* Refer to comments above for the suspend mode ops */ + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_scale(data, val2); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static int bmg160_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static int bmg160_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (data->ev_enable_state) + return -EBUSY; + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bmg160_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + + struct bmg160_data *data = iio_priv(indio_dev); + + return data->ev_enable_state; +} + +static int bmg160_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + if (state && data->ev_enable_state) + return 0; + + mutex_lock(&data->mutex); + + if (!state && data->motion_trigger_on) { + data->ev_enable_state = 0; + mutex_unlock(&data->mutex); + return 0; + } + /* + * We will expect the enable and disable to do operation in + * in reverse order. This will happen here anyway as our + * resume operation uses sync mode runtime pm calls, the + * suspend operation will be delayed by autosuspend delay + * So the disable operation will still happen in reverse of + * enable operation. When runtime pm is disabled the mode + * is always on so sequence doesn't matter + */ + ret = bmg160_set_power_state(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = bmg160_setup_any_motion_interrupt(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + data->ev_enable_state = state; + mutex_unlock(&data->mutex); + + return 0; +} + +static int bmg160_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + if (data->dready_trig != trig && data->motion_trig != trig) + return -EINVAL; + + return 0; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); + +static IIO_CONST_ATTR(in_anglvel_scale_available, + "0.001065 0.000532 0.000266 0.000133 0.000066"); + +static struct attribute *bmg160_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bmg160_attrs_group = { + .attrs = bmg160_attributes, +}; + +static const struct iio_event_spec bmg160_event = { + .type = IIO_EV_TYPE_ROC, + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) +}; + +#define BMG160_CHANNEL(_axis) { \ + .type = IIO_ANGL_VEL, \ + .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_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + .event_spec = &bmg160_event, \ + .num_event_specs = 1 \ +} + +static const struct iio_chan_spec bmg160_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, + }, + BMG160_CHANNEL(X), + BMG160_CHANNEL(Y), + BMG160_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_info bmg160_info = { + .attrs = &bmg160_attrs_group, + .read_raw = bmg160_read_raw, + .write_raw = bmg160_write_raw, + .read_event_value = bmg160_read_event, + .write_event_value = bmg160_write_event, + .write_event_config = bmg160_write_event_config, + .read_event_config = bmg160_read_event_config, + .validate_trigger = bmg160_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t bmg160_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmg160_data *data = iio_priv(indio_dev); + int bit, ret, i = 0; + + mutex_lock(&data->mutex); + for_each_set_bit(bit, indio_dev->buffer->scan_mask, + indio_dev->masklength) { + ret = i2c_smbus_read_word_data(data->client, + BMG160_AXIS_TO_REG(bit)); + if (ret < 0) { + mutex_unlock(&data->mutex); + goto err; + } + data->buffer[i++] = ret; + } + mutex_unlock(&data->mutex); + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + data->timestamp); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int bmg160_trig_try_reen(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + /* new data interrupts don't need ack */ + if (data->dready_trigger_on) + return 0; + + /* Set latched mode interrupt and clear any latched interrupt */ + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); + return ret; + } + + return 0; +} + +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + + if (!state && data->ev_enable_state && data->motion_trigger_on) { + data->motion_trigger_on = false; + mutex_unlock(&data->mutex); + return 0; + } + + /* + * Refer to comment in bmg160_write_event_config for + * enable/disable operation order + */ + ret = bmg160_set_power_state(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + if (data->motion_trig == trig) + ret = bmg160_setup_any_motion_interrupt(data, state); + else + ret = bmg160_setup_new_data_interrupt(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + if (data->motion_trig == trig) + data->motion_trigger_on = state; + else + data->dready_trigger_on = state; + + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_trigger_ops bmg160_trigger_ops = { + .set_trigger_state = bmg160_data_rdy_trigger_set_state, + .try_reenable = bmg160_trig_try_reen, + .owner = THIS_MODULE, +}; + +static irqreturn_t bmg160_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + int dir; + + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); + goto ack_intr_status; + } + + if (ret & 0x08) + dir = IIO_EV_DIR_RISING; + else + dir = IIO_EV_DIR_FALLING; + + if (ret & BMG160_ANY_MOTION_MASK) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + +ack_intr_status: + if (!data->dready_trigger_on) { + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) + dev_err(&data->client->dev, + "Error writing reg_rst_latch\n"); + } + + return IRQ_HANDLED; +} + +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmg160_data *data = iio_priv(indio_dev); + + data->timestamp = iio_get_time_ns(); + + if (data->dready_trigger_on) + iio_trigger_poll(data->dready_trig); + else if (data->motion_trigger_on) + iio_trigger_poll(data->motion_trig); + + if (data->ev_enable_state) + return IRQ_WAKE_THREAD; + else + return IRQ_HANDLED; + +} + +static int bmg160_acpi_gpio_probe(struct i2c_client *client, + struct bmg160_data *data) +{ + const struct acpi_device_id *id; + struct device *dev; + struct gpio_desc *gpio; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + if (!ACPI_HANDLE(dev)) + return -ENODEV; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* data ready gpio interrupt pin */ + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_direction_input(gpio); + if (ret) + return ret; + + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + + return ret; +} + +static int bmg160_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bmg160_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + ret = bmg160_chip_init(data); + if (ret < 0) + return ret; + + mutex_init(&data->mutex); + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = bmg160_channels; + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); + indio_dev->name = BMG160_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmg160_info; + + if (client->irq <= 0) + client->irq = bmg160_acpi_gpio_probe(client, data); + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, + client->irq, + bmg160_data_rdy_trig_poll, + bmg160_event_handler, + IRQF_TRIGGER_RISING, + BMG160_IRQ_NAME, + indio_dev); + if (ret) + return ret; + + data->dready_trig = devm_iio_trigger_alloc(&client->dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!data->dready_trig) + return -ENOMEM; + + data->motion_trig = devm_iio_trigger_alloc(&client->dev, + "%s-any-motion-dev%d", + indio_dev->name, + indio_dev->id); + if (!data->motion_trig) + return -ENOMEM; + + data->dready_trig->dev.parent = &client->dev; + data->dready_trig->ops = &bmg160_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + ret = iio_trigger_register(data->dready_trig); + if (ret) + return ret; + + data->motion_trig->dev.parent = &client->dev; + data->motion_trig->ops = &bmg160_trigger_ops; + iio_trigger_set_drvdata(data->motion_trig, indio_dev); + ret = iio_trigger_register(data->motion_trig); + if (ret) { + data->motion_trig = NULL; + goto err_trigger_unregister; + } + + ret = iio_triggered_buffer_setup(indio_dev, + NULL, + bmg160_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&client->dev, + "iio triggered buffer setup failed\n"); + goto err_trigger_unregister; + } + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + + ret = pm_runtime_set_active(&client->dev); + if (ret) + goto err_iio_unregister; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + BMG160_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return 0; + +err_iio_unregister: + iio_device_unregister(indio_dev); +err_buffer_cleanup: + if (data->dready_trig) + iio_triggered_buffer_cleanup(indio_dev); +err_trigger_unregister: + if (data->dready_trig) + iio_trigger_unregister(data->dready_trig); + if (data->motion_trig) + iio_trigger_unregister(data->motion_trig); + + return ret; +} + +static int bmg160_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct bmg160_data *data = iio_priv(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + iio_device_unregister(indio_dev); + + if (data->dready_trig) { + iio_triggered_buffer_cleanup(indio_dev); + iio_trigger_unregister(data->dready_trig); + iio_trigger_unregister(data->motion_trig); + } + + mutex_lock(&data->mutex); + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); + mutex_unlock(&data->mutex); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bmg160_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct bmg160_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + bmg160_set_mode(data, BMG160_MODE_SUSPEND); + mutex_unlock(&data->mutex); + + return 0; +} + +static int bmg160_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct bmg160_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + if (data->dready_trigger_on || data->motion_trigger_on || + data->ev_enable_state) + bmg160_set_mode(data, BMG160_MODE_NORMAL); + mutex_unlock(&data->mutex); + + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int bmg160_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct bmg160_data *data = iio_priv(indio_dev); + + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); +} + +static int bmg160_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); + if (ret < 0) + return ret; + + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); + + return 0; +} +#endif + +static const struct dev_pm_ops bmg160_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, + bmg160_runtime_resume, NULL) +}; + +static const struct acpi_device_id bmg160_acpi_match[] = { + {"BMG0160", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); + +static const struct i2c_device_id bmg160_id[] = { + {"bmg160", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bmg160_id); + +static struct i2c_driver bmg160_driver = { + .driver = { + .name = BMG160_DRV_NAME, + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), + .pm = &bmg160_pm_ops, + }, + .probe = bmg160_probe, + .remove = bmg160_remove, + .id_table = bmg160_id, +}; +module_i2c_driver(bmg160_driver); + +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMG160 Gyro driver"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-05 21:57 ` [PATCH v3 1/2] " Srinivas Pandruvada @ 2014-08-07 10:39 ` Jonathan Cameron 2014-08-08 2:47 ` Srinivas Pandruvada 2014-08-12 19:06 ` Srinivas Pandruvada 0 siblings, 2 replies; 8+ messages in thread From: Jonathan Cameron @ 2014-08-07 10:39 UTC (permalink / raw) To: Srinivas Pandruvada; +Cc: linux-iio On 05/08/14 22:57, Srinivas Pandruvada wrote: > This change implements support for BMG160 Gyro sensor. Although chip > has several advanced features, this change implements minimum set > required for using gyro sensor. > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> One more query. In the write_raw function you always enable then suspend the device. The enable is safe, but if you have a write to the attributes whilst operating in triggered / buffered mode will it not disable the device? It is perfectly permissible to ignore such a write (return -EBUSY) but I don't think you are doing so. Otherwise, looks good to me. Nice explanatory comments as well! > --- > drivers/iio/gyro/Kconfig | 11 + > drivers/iio/gyro/Makefile | 1 + > drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1223 insertions(+) > create mode 100644 drivers/iio/gyro/bmg160.c > > diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig > index ac2d69e..d630ae9 100644 > --- a/drivers/iio/gyro/Kconfig > +++ b/drivers/iio/gyro/Kconfig > @@ -50,6 +50,17 @@ config ADXRS450 > This driver can also be built as a module. If so, the module > will be called adxrs450. > > +config BMG160 > + tristate "BOSCH BMG160 Gyro Sensor" > + depends on I2C > + select IIO_TRIGGERED_BUFFER if IIO_BUFFER > + help > + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor > + driver. > + > + This driver can also be built as a module. If so, the module > + will be called bmg160. > + > config HID_SENSOR_GYRO_3D > depends on HID_SENSOR_HUB > select IIO_BUFFER > diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile > index 2f2752a..36a3877 100644 > --- a/drivers/iio/gyro/Makefile > +++ b/drivers/iio/gyro/Makefile > @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o > obj-$(CONFIG_ADIS16136) += adis16136.o > obj-$(CONFIG_ADIS16260) += adis16260.o > obj-$(CONFIG_ADXRS450) += adxrs450.o > +obj-$(CONFIG_BMG160) += bmg160.o > > obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o > > diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c > new file mode 100644 > index 0000000..80f92a6 > --- /dev/null > +++ b/drivers/iio/gyro/bmg160.c > @@ -0,0 +1,1211 @@ > +/* > + * BMG160 Gyro Sensor driver > + * Copyright (c) 2014, Intel Corporation. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + */ > + > +#include <linux/module.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/delay.h> > +#include <linux/slab.h> > +#include <linux/acpi.h> > +#include <linux/gpio/consumer.h> > +#include <linux/pm.h> > +#include <linux/pm_runtime.h> > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/iio/buffer.h> > +#include <linux/iio/trigger.h> > +#include <linux/iio/events.h> > +#include <linux/iio/trigger_consumer.h> > +#include <linux/iio/triggered_buffer.h> > + > +#define BMG160_DRV_NAME "bmg160" > +#define BMG160_IRQ_NAME "bmg160_event" > +#define BMG160_GPIO_NAME "gpio_int" > + > +#define BMG160_REG_CHIP_ID 0x00 > +#define BMG160_CHIP_ID_VAL 0x0F > + > +#define BMG160_REG_PMU_LPW 0x11 > +#define BMG160_MODE_NORMAL 0x00 > +#define BMG160_MODE_DEEP_SUSPEND 0x20 > +#define BMG160_MODE_SUSPEND 0x80 > + > +#define BMG160_REG_RANGE 0x0F > + > +#define BMG160_RANGE_2000DPS 0 > +#define BMG160_RANGE_1000DPS 1 > +#define BMG160_RANGE_500DPS 2 > +#define BMG160_RANGE_250DPS 3 > +#define BMG160_RANGE_125DPS 4 > + > +#define BMG160_REG_PMU_BW 0x10 > +#define BMG160_NO_FILTER 0 > +#define BMG160_DEF_BW 100 > + > +#define BMG160_REG_INT_MAP_0 0x17 > +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) > + > +#define BMG160_REG_INT_MAP_1 0x18 > +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) > + > +#define BMG160_REG_INT_RST_LATCH 0x21 > +#define BMG160_INT_MODE_LATCH_RESET 0x80 > +#define BMG160_INT_MODE_LATCH_INT 0x0F > +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 > + > +#define BMG160_REG_INT_EN_0 0x15 > +#define BMG160_DATA_ENABLE_INT BIT(7) > + > +#define BMG160_REG_XOUT_L 0x02 > +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) > + > +#define BMG160_REG_SLOPE_THRES 0x1B > +#define BMG160_SLOPE_THRES_MASK 0x0F > + > +#define BMG160_REG_MOTION_INTR 0x1C > +#define BMG160_INT_MOTION_X BIT(0) > +#define BMG160_INT_MOTION_Y BIT(1) > +#define BMG160_INT_MOTION_Z BIT(2) > +#define BMG160_ANY_DUR_MASK 0x30 > +#define BMG160_ANY_DUR_SHIFT 4 > + > +#define BMG160_REG_INT_STATUS_2 0x0B > +#define BMG160_ANY_MOTION_MASK 0x07 > + > +#define BMG160_REG_TEMP 0x08 > +#define BMG160_TEMP_CENTER_VAL 23 > + > +#define BMG160_MAX_STARTUP_TIME_MS 80 > + > +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 > + > +struct bmg160_data { > + struct i2c_client *client; > + struct iio_trigger *dready_trig; > + struct iio_trigger *motion_trig; > + struct mutex mutex; > + s16 buffer[8]; > + u8 bw_bits; > + u32 dps_range; > + int ev_enable_state; > + int slope_thres; > + bool dready_trigger_on; > + bool motion_trigger_on; > + int64_t timestamp; > +}; > + > +enum bmg160_axis { > + AXIS_X, > + AXIS_Y, > + AXIS_Z, > +}; > + > +static const struct { > + int val; > + int bw_bits; > +} bmg160_samp_freq_table[] = { {100, 0x07}, > + {200, 0x06}, > + {400, 0x03}, > + {1000, 0x02}, > + {2000, 0x01} }; > + > +static const struct { > + int scale; > + int dps_range; > +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, > + { 532, BMG160_RANGE_1000DPS}, > + { 266, BMG160_RANGE_500DPS}, > + { 133, BMG160_RANGE_250DPS}, > + { 66, BMG160_RANGE_125DPS} }; > + > +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_PMU_LPW, mode); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_convert_freq_to_bit(int val) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { > + if (bmg160_samp_freq_table[i].val == val) > + return bmg160_samp_freq_table[i].bw_bits; > + } > + > + return -EINVAL; > +} > + > +static int bmg160_set_bw(struct bmg160_data *data, int val) > +{ > + int ret; > + int bw_bits; > + > + bw_bits = bmg160_convert_freq_to_bit(val); > + if (bw_bits < 0) > + return bw_bits; > + > + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, > + bw_bits); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); > + return ret; > + } > + > + data->bw_bits = bw_bits; > + > + return 0; > +} > + > +static int bmg160_chip_init(struct bmg160_data *data) > +{ > + int ret; > + > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); > + return ret; > + } > + > + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); > + if (ret != BMG160_CHIP_ID_VAL) { > + dev_err(&data->client->dev, "invalid chip %x\n", ret); > + return -ENODEV; > + } > + > + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); > + if (ret < 0) > + return ret; > + > + /* Wait upto 500 ms to be ready after changing mode */ > + usleep_range(500, 1000); > + > + /* Set Bandwidth */ > + ret = bmg160_set_bw(data, BMG160_DEF_BW); > + if (ret < 0) > + return ret; > + > + /* Set Default Range */ > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_RANGE, > + BMG160_RANGE_500DPS); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_range\n"); > + return ret; > + } > + data->dps_range = BMG160_RANGE_500DPS; > + > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); > + return ret; > + } > + data->slope_thres = ret; > + > + /* Set default interrupt mode */ > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_motion_intr\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_set_power_state(struct bmg160_data *data, bool on) > +{ > + int ret; > + > + if (on) > + ret = pm_runtime_get_sync(&data->client->dev); > + else { > + pm_runtime_mark_last_busy(&data->client->dev); > + ret = pm_runtime_put_autosuspend(&data->client->dev); > + } > + > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Failed: bmg160_set_power_state for %d\n", on); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, > + bool status) > +{ > + int ret; > + > + /* Enable/Disable INT_MAP0 mapping */ > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); > + return ret; > + } > + if (status) > + ret |= BMG160_INT_MAP_0_BIT_ANY; > + else > + ret &= ~BMG160_INT_MAP_0_BIT_ANY; > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_MAP_0, > + ret); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); > + return ret; > + } > + > + /* Enable/Disable slope interrupts */ > + if (status) { > + /* Update slope thres */ > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_SLOPE_THRES, > + data->slope_thres); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_slope_thres\n"); > + return ret; > + } > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_MOTION_INTR, > + BMG160_INT_MOTION_X | > + BMG160_INT_MOTION_Y | > + BMG160_INT_MOTION_Z); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_motion_intr\n"); > + return ret; > + } > + > + /* > + * New data interrupt is always non-latched, > + * which will have higher priority, so no need > + * to set latched mode, we will be flooded anyway with INTR > + */ > + if (!data->dready_trigger_on) { > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_rst_latch\n"); > + return ret; > + } > + } > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_EN_0, > + BMG160_DATA_ENABLE_INT); > + > + } else > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_EN_0, > + 0); > + > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, > + bool status) > +{ > + int ret; > + > + /* Enable/Disable INT_MAP1 mapping */ > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); > + return ret; > + } > + > + if (status) > + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; > + else > + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_MAP_1, > + ret); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); > + return ret; > + } > + > + if (status) { > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_NON_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_rst_latch\n"); > + return ret; > + } > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_EN_0, > + BMG160_DATA_ENABLE_INT); > + > + } else { > + /* Restore interrupt mode */ > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_rst_latch\n"); > + return ret; > + } > + > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_EN_0, > + 0); > + } > + > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_get_bw(struct bmg160_data *data, int *val) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { > + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { > + *val = bmg160_samp_freq_table[i].val; > + return IIO_VAL_INT; > + } > + } > + > + return -EINVAL; > +} > + > +static int bmg160_set_scale(struct bmg160_data *data, int val) > +{ > + int ret, i; > + > + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { > + if (bmg160_scale_table[i].scale == val) { > + ret = i2c_smbus_write_byte_data( > + data->client, > + BMG160_REG_RANGE, > + bmg160_scale_table[i].dps_range); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "Error writing reg_range\n"); > + return ret; > + } > + data->dps_range = bmg160_scale_table[i].dps_range; > + return 0; > + } > + } > + > + return -EINVAL; > +} > + > +static int bmg160_get_temp(struct bmg160_data *data, int *val) > +{ > + int ret; > + > + mutex_lock(&data->mutex); > + ret = bmg160_set_power_state(data, true); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_temp\n"); > + bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + *val = sign_extend32(ret, 7); > + ret = bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + if (ret < 0) > + return ret; > + > + return IIO_VAL_INT; > +} > + > +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) > +{ > + int ret; > + > + mutex_lock(&data->mutex); > + ret = bmg160_set_power_state(data, true); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading axis %d\n", axis); > + bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + *val = sign_extend32(ret, 15); > + ret = bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + if (ret < 0) > + return ret; > + > + return IIO_VAL_INT; > +} > + > +static int bmg160_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + switch (chan->type) { > + case IIO_TEMP: > + return bmg160_get_temp(data, val); > + case IIO_ANGL_VEL: > + if (iio_buffer_enabled(indio_dev)) > + return -EBUSY; > + else > + return bmg160_get_axis(data, chan->scan_index, > + val); > + default: > + return -EINVAL; > + } > + case IIO_CHAN_INFO_OFFSET: > + if (chan->type == IIO_TEMP) { > + *val = BMG160_TEMP_CENTER_VAL; > + return IIO_VAL_INT; > + } else > + return -EINVAL; > + case IIO_CHAN_INFO_SCALE: > + *val = 0; > + switch (chan->type) { > + case IIO_TEMP: > + *val2 = 500000; > + return IIO_VAL_INT_PLUS_MICRO; > + case IIO_ANGL_VEL: > + { > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { > + if (bmg160_scale_table[i].dps_range == > + data->dps_range) { > + *val2 = bmg160_scale_table[i].scale; > + return IIO_VAL_INT_PLUS_MICRO; > + } > + } > + return -EINVAL; > + } > + default: > + return -EINVAL; > + } > + case IIO_CHAN_INFO_SAMP_FREQ: > + *val2 = 0; > + mutex_lock(&data->mutex); > + ret = bmg160_get_bw(data, val); > + mutex_unlock(&data->mutex); > + return ret; > + default: > + return -EINVAL; > + } > +} > + > +static int bmg160_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_SAMP_FREQ: > + mutex_lock(&data->mutex); > + /* > + * Section 4.2 of spec > + * In suspend mode, the only supported operations are reading > + * registers as well as writing to the (0x14) softreset > + * register. Since we will be in suspend mode by default, change > + * mode to power on for other writes. > + */ > + ret = bmg160_set_power_state(data, true); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + ret = bmg160_set_bw(data, val); > + if (ret < 0) { > + bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + return ret; > + } > + ret = bmg160_set_power_state(data, false); Could this end up disabling the part when in triggered mode? > + mutex_unlock(&data->mutex); > + return ret; > + case IIO_CHAN_INFO_SCALE: > + if (val) > + return -EINVAL; > + > + mutex_lock(&data->mutex); > + /* Refer to comments above for the suspend mode ops */ > + ret = bmg160_set_power_state(data, true); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + ret = bmg160_set_scale(data, val2); > + if (ret < 0) { > + bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + return ret; > + } > + ret = bmg160_set_power_state(data, false); > + mutex_unlock(&data->mutex); > + return ret; > + default: > + return -EINVAL; > + } > + > + return -EINVAL; > +} > + > +static int bmg160_read_event(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + enum iio_event_info info, > + int *val, int *val2) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + > + *val2 = 0; > + switch (info) { > + case IIO_EV_INFO_VALUE: > + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; > + break; > + default: > + return -EINVAL; > + } > + > + return IIO_VAL_INT; > +} > + > +static int bmg160_write_event(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + enum iio_event_info info, > + int val, int val2) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + > + switch (info) { > + case IIO_EV_INFO_VALUE: > + if (data->ev_enable_state) > + return -EBUSY; > + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; > + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int bmg160_read_event_config(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir) > +{ > + > + struct bmg160_data *data = iio_priv(indio_dev); > + > + return data->ev_enable_state; > +} > + > +static int bmg160_write_event_config(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + int state) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + if (state && data->ev_enable_state) > + return 0; > + > + mutex_lock(&data->mutex); > + > + if (!state && data->motion_trigger_on) { > + data->ev_enable_state = 0; > + mutex_unlock(&data->mutex); > + return 0; > + } > + /* > + * We will expect the enable and disable to do operation in > + * in reverse order. This will happen here anyway as our > + * resume operation uses sync mode runtime pm calls, the > + * suspend operation will be delayed by autosuspend delay > + * So the disable operation will still happen in reverse of > + * enable operation. When runtime pm is disabled the mode > + * is always on so sequence doesn't matter > + */ Thanks for the detailed comment! > + ret = bmg160_set_power_state(data, state); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + ret = bmg160_setup_any_motion_interrupt(data, state); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + > + data->ev_enable_state = state; > + mutex_unlock(&data->mutex); > + > + return 0; > +} > + > +static int bmg160_validate_trigger(struct iio_dev *indio_dev, > + struct iio_trigger *trig) > +{ > + struct bmg160_data *data = iio_priv(indio_dev); > + > + if (data->dready_trig != trig && data->motion_trig != trig) > + return -EINVAL; > + > + return 0; > +} > + > +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); > + > +static IIO_CONST_ATTR(in_anglvel_scale_available, > + "0.001065 0.000532 0.000266 0.000133 0.000066"); > + > +static struct attribute *bmg160_attributes[] = { > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group bmg160_attrs_group = { > + .attrs = bmg160_attributes, > +}; > + > +static const struct iio_event_spec bmg160_event = { > + .type = IIO_EV_TYPE_ROC, > + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, > + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | > + BIT(IIO_EV_INFO_ENABLE) > +}; > + > +#define BMG160_CHANNEL(_axis) { \ > + .type = IIO_ANGL_VEL, \ > + .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_index = AXIS_##_axis, \ > + .scan_type = { \ > + .sign = 's', \ > + .realbits = 16, \ > + .storagebits = 16, \ > + }, \ > + .event_spec = &bmg160_event, \ > + .num_event_specs = 1 \ > +} > + > +static const struct iio_chan_spec bmg160_channels[] = { > + { > + .type = IIO_TEMP, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_SCALE) | > + BIT(IIO_CHAN_INFO_OFFSET), > + .scan_index = -1, > + }, > + BMG160_CHANNEL(X), > + BMG160_CHANNEL(Y), > + BMG160_CHANNEL(Z), > + IIO_CHAN_SOFT_TIMESTAMP(3), > +}; > + > +static const struct iio_info bmg160_info = { > + .attrs = &bmg160_attrs_group, > + .read_raw = bmg160_read_raw, > + .write_raw = bmg160_write_raw, > + .read_event_value = bmg160_read_event, > + .write_event_value = bmg160_write_event, > + .write_event_config = bmg160_write_event_config, > + .read_event_config = bmg160_read_event_config, > + .validate_trigger = bmg160_validate_trigger, > + .driver_module = THIS_MODULE, > +}; > + > +static irqreturn_t bmg160_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct bmg160_data *data = iio_priv(indio_dev); > + int bit, ret, i = 0; > + > + mutex_lock(&data->mutex); > + for_each_set_bit(bit, indio_dev->buffer->scan_mask, > + indio_dev->masklength) { > + ret = i2c_smbus_read_word_data(data->client, > + BMG160_AXIS_TO_REG(bit)); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + goto err; > + } > + data->buffer[i++] = ret; > + } > + mutex_unlock(&data->mutex); > + > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, > + data->timestamp); > +err: > + iio_trigger_notify_done(indio_dev->trig); > + > + return IRQ_HANDLED; > +} > + > +static int bmg160_trig_try_reen(struct iio_trigger *trig) > +{ > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + /* new data interrupts don't need ack */ > + if (data->dready_trigger_on) > + return 0; > + > + /* Set latched mode interrupt and clear any latched interrupt */ > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, > + bool state) > +{ > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + mutex_lock(&data->mutex); > + > + if (!state && data->ev_enable_state && data->motion_trigger_on) { > + data->motion_trigger_on = false; > + mutex_unlock(&data->mutex); > + return 0; > + } > + > + /* > + * Refer to comment in bmg160_write_event_config for > + * enable/disable operation order > + */ > + ret = bmg160_set_power_state(data, state); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + if (data->motion_trig == trig) > + ret = bmg160_setup_any_motion_interrupt(data, state); > + else > + ret = bmg160_setup_new_data_interrupt(data, state); > + if (ret < 0) { > + mutex_unlock(&data->mutex); > + return ret; > + } > + if (data->motion_trig == trig) > + data->motion_trigger_on = state; > + else > + data->dready_trigger_on = state; > + > + mutex_unlock(&data->mutex); > + > + return 0; > +} > + > +static const struct iio_trigger_ops bmg160_trigger_ops = { > + .set_trigger_state = bmg160_data_rdy_trigger_set_state, > + .try_reenable = bmg160_trig_try_reen, > + .owner = THIS_MODULE, > +}; > + > +static irqreturn_t bmg160_event_handler(int irq, void *private) > +{ > + struct iio_dev *indio_dev = private; > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + int dir; > + > + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); > + if (ret < 0) { > + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); > + goto ack_intr_status; > + } > + > + if (ret & 0x08) > + dir = IIO_EV_DIR_RISING; > + else > + dir = IIO_EV_DIR_FALLING; > + > + if (ret & BMG160_ANY_MOTION_MASK) > + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, > + 0, > + IIO_MOD_X_OR_Y_OR_Z, > + IIO_EV_TYPE_ROC, > + dir), > + data->timestamp); > + > +ack_intr_status: > + if (!data->dready_trigger_on) { > + ret = i2c_smbus_write_byte_data(data->client, > + BMG160_REG_INT_RST_LATCH, > + BMG160_INT_MODE_LATCH_INT | > + BMG160_INT_MODE_LATCH_RESET); > + if (ret < 0) > + dev_err(&data->client->dev, > + "Error writing reg_rst_latch\n"); > + } > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) > +{ > + struct iio_dev *indio_dev = private; > + struct bmg160_data *data = iio_priv(indio_dev); > + > + data->timestamp = iio_get_time_ns(); > + > + if (data->dready_trigger_on) > + iio_trigger_poll(data->dready_trig); > + else if (data->motion_trigger_on) > + iio_trigger_poll(data->motion_trig); > + > + if (data->ev_enable_state) > + return IRQ_WAKE_THREAD; > + else > + return IRQ_HANDLED; > + > +} > + > +static int bmg160_acpi_gpio_probe(struct i2c_client *client, > + struct bmg160_data *data) > +{ > + const struct acpi_device_id *id; > + struct device *dev; > + struct gpio_desc *gpio; > + int ret; > + > + if (!client) > + return -EINVAL; > + > + dev = &client->dev; > + if (!ACPI_HANDLE(dev)) > + return -ENODEV; > + > + id = acpi_match_device(dev->driver->acpi_match_table, dev); > + if (!id) > + return -ENODEV; > + > + /* data ready gpio interrupt pin */ > + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); > + if (IS_ERR(gpio)) { > + dev_err(dev, "acpi gpio get index failed\n"); > + return PTR_ERR(gpio); > + } > + > + ret = gpiod_direction_input(gpio); > + if (ret) > + return ret; > + > + ret = gpiod_to_irq(gpio); > + > + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); > + > + return ret; > +} > + > +static int bmg160_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct bmg160_data *data; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + > + ret = bmg160_chip_init(data); > + if (ret < 0) > + return ret; > + > + mutex_init(&data->mutex); > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->channels = bmg160_channels; > + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); > + indio_dev->name = BMG160_DRV_NAME; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &bmg160_info; > + > + if (client->irq <= 0) > + client->irq = bmg160_acpi_gpio_probe(client, data); > + > + if (client->irq > 0) { > + ret = devm_request_threaded_irq(&client->dev, > + client->irq, > + bmg160_data_rdy_trig_poll, > + bmg160_event_handler, > + IRQF_TRIGGER_RISING, > + BMG160_IRQ_NAME, > + indio_dev); > + if (ret) > + return ret; > + > + data->dready_trig = devm_iio_trigger_alloc(&client->dev, > + "%s-dev%d", > + indio_dev->name, > + indio_dev->id); > + if (!data->dready_trig) > + return -ENOMEM; > + > + data->motion_trig = devm_iio_trigger_alloc(&client->dev, > + "%s-any-motion-dev%d", > + indio_dev->name, > + indio_dev->id); > + if (!data->motion_trig) > + return -ENOMEM; > + > + data->dready_trig->dev.parent = &client->dev; > + data->dready_trig->ops = &bmg160_trigger_ops; > + iio_trigger_set_drvdata(data->dready_trig, indio_dev); > + ret = iio_trigger_register(data->dready_trig); > + if (ret) > + return ret; > + > + data->motion_trig->dev.parent = &client->dev; > + data->motion_trig->ops = &bmg160_trigger_ops; > + iio_trigger_set_drvdata(data->motion_trig, indio_dev); > + ret = iio_trigger_register(data->motion_trig); > + if (ret) { > + data->motion_trig = NULL; > + goto err_trigger_unregister; > + } > + > + ret = iio_triggered_buffer_setup(indio_dev, > + NULL, > + bmg160_trigger_handler, > + NULL); > + if (ret < 0) { > + dev_err(&client->dev, > + "iio triggered buffer setup failed\n"); > + goto err_trigger_unregister; > + } > + } > + > + ret = iio_device_register(indio_dev); > + if (ret < 0) { > + dev_err(&client->dev, "unable to register iio device\n"); > + goto err_buffer_cleanup; > + } > + > + ret = pm_runtime_set_active(&client->dev); > + if (ret) > + goto err_iio_unregister; > + > + pm_runtime_enable(&client->dev); > + pm_runtime_set_autosuspend_delay(&client->dev, > + BMG160_AUTO_SUSPEND_DELAY_MS); > + pm_runtime_use_autosuspend(&client->dev); > + > + return 0; > + > +err_iio_unregister: > + iio_device_unregister(indio_dev); > +err_buffer_cleanup: > + if (data->dready_trig) > + iio_triggered_buffer_cleanup(indio_dev); > +err_trigger_unregister: > + if (data->dready_trig) > + iio_trigger_unregister(data->dready_trig); > + if (data->motion_trig) > + iio_trigger_unregister(data->motion_trig); > + > + return ret; > +} > + > +static int bmg160_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct bmg160_data *data = iio_priv(indio_dev); > + > + pm_runtime_disable(&client->dev); > + pm_runtime_set_suspended(&client->dev); > + pm_runtime_put_noidle(&client->dev); > + > + iio_device_unregister(indio_dev); > + > + if (data->dready_trig) { > + iio_triggered_buffer_cleanup(indio_dev); > + iio_trigger_unregister(data->dready_trig); > + iio_trigger_unregister(data->motion_trig); > + } > + > + mutex_lock(&data->mutex); > + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); > + mutex_unlock(&data->mutex); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int bmg160_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct bmg160_data *data = iio_priv(indio_dev); > + > + mutex_lock(&data->mutex); > + bmg160_set_mode(data, BMG160_MODE_SUSPEND); > + mutex_unlock(&data->mutex); > + > + return 0; > +} > + > +static int bmg160_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct bmg160_data *data = iio_priv(indio_dev); > + > + mutex_lock(&data->mutex); > + if (data->dready_trigger_on || data->motion_trigger_on || > + data->ev_enable_state) > + bmg160_set_mode(data, BMG160_MODE_NORMAL); > + mutex_unlock(&data->mutex); > + > + return 0; > +} > +#endif > + > +#ifdef CONFIG_PM_RUNTIME > +static int bmg160_runtime_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct bmg160_data *data = iio_priv(indio_dev); > + > + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); > +} > + > +static int bmg160_runtime_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct bmg160_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); > + if (ret < 0) > + return ret; > + > + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); > + > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops bmg160_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) > + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, > + bmg160_runtime_resume, NULL) > +}; > + > +static const struct acpi_device_id bmg160_acpi_match[] = { > + {"BMG0160", 0}, > + { }, > +}; > +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); > + > +static const struct i2c_device_id bmg160_id[] = { > + {"bmg160", 0}, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, bmg160_id); > + > +static struct i2c_driver bmg160_driver = { > + .driver = { > + .name = BMG160_DRV_NAME, > + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), > + .pm = &bmg160_pm_ops, > + }, > + .probe = bmg160_probe, > + .remove = bmg160_remove, > + .id_table = bmg160_id, > +}; > +module_i2c_driver(bmg160_driver); > + > +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("BMG160 Gyro driver"); > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-07 10:39 ` Jonathan Cameron @ 2014-08-08 2:47 ` Srinivas Pandruvada 2014-08-12 19:06 ` Srinivas Pandruvada 1 sibling, 0 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2014-08-08 2:47 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio On 08/07/2014 03:39 AM, Jonathan Cameron wrote: > On 05/08/14 22:57, Srinivas Pandruvada wrote: >> This change implements support for BMG160 Gyro sensor. Although chip >> has several advanced features, this change implements minimum set >> required for using gyro sensor. >> >> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> > One more query. In the write_raw function you always enable then suspend the > device. The enable is safe, but if you have a write to the attributes > whilst operating in triggered / buffered mode will it not disable the > device? It is perfectly permissible to ignore such a write (return -EBUSY) > but I don't think you are doing so. I will check on this. Thanks, Srinivas > > Otherwise, looks good to me. Nice explanatory comments as well! >> --- >> drivers/iio/gyro/Kconfig | 11 + >> drivers/iio/gyro/Makefile | 1 + >> drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 1223 insertions(+) >> create mode 100644 drivers/iio/gyro/bmg160.c >> >> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig >> index ac2d69e..d630ae9 100644 >> --- a/drivers/iio/gyro/Kconfig >> +++ b/drivers/iio/gyro/Kconfig >> @@ -50,6 +50,17 @@ config ADXRS450 >> This driver can also be built as a module. If so, the module >> will be called adxrs450. >> >> +config BMG160 >> + tristate "BOSCH BMG160 Gyro Sensor" >> + depends on I2C >> + select IIO_TRIGGERED_BUFFER if IIO_BUFFER >> + help >> + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor >> + driver. >> + >> + This driver can also be built as a module. If so, the module >> + will be called bmg160. >> + >> config HID_SENSOR_GYRO_3D >> depends on HID_SENSOR_HUB >> select IIO_BUFFER >> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile >> index 2f2752a..36a3877 100644 >> --- a/drivers/iio/gyro/Makefile >> +++ b/drivers/iio/gyro/Makefile >> @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o >> obj-$(CONFIG_ADIS16136) += adis16136.o >> obj-$(CONFIG_ADIS16260) += adis16260.o >> obj-$(CONFIG_ADXRS450) += adxrs450.o >> +obj-$(CONFIG_BMG160) += bmg160.o >> >> obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o >> >> diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c >> new file mode 100644 >> index 0000000..80f92a6 >> --- /dev/null >> +++ b/drivers/iio/gyro/bmg160.c >> @@ -0,0 +1,1211 @@ >> +/* >> + * BMG160 Gyro Sensor driver >> + * Copyright (c) 2014, Intel Corporation. >> + * >> + * This program is free software; you can redistribute it and/or modify it >> + * under the terms and conditions of the GNU General Public License, >> + * version 2, as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope it will be useful, but WITHOUT >> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for >> + * more details. >> + */ >> + >> +#include <linux/module.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/delay.h> >> +#include <linux/slab.h> >> +#include <linux/acpi.h> >> +#include <linux/gpio/consumer.h> >> +#include <linux/pm.h> >> +#include <linux/pm_runtime.h> >> +#include <linux/iio/iio.h> >> +#include <linux/iio/sysfs.h> >> +#include <linux/iio/buffer.h> >> +#include <linux/iio/trigger.h> >> +#include <linux/iio/events.h> >> +#include <linux/iio/trigger_consumer.h> >> +#include <linux/iio/triggered_buffer.h> >> + >> +#define BMG160_DRV_NAME "bmg160" >> +#define BMG160_IRQ_NAME "bmg160_event" >> +#define BMG160_GPIO_NAME "gpio_int" >> + >> +#define BMG160_REG_CHIP_ID 0x00 >> +#define BMG160_CHIP_ID_VAL 0x0F >> + >> +#define BMG160_REG_PMU_LPW 0x11 >> +#define BMG160_MODE_NORMAL 0x00 >> +#define BMG160_MODE_DEEP_SUSPEND 0x20 >> +#define BMG160_MODE_SUSPEND 0x80 >> + >> +#define BMG160_REG_RANGE 0x0F >> + >> +#define BMG160_RANGE_2000DPS 0 >> +#define BMG160_RANGE_1000DPS 1 >> +#define BMG160_RANGE_500DPS 2 >> +#define BMG160_RANGE_250DPS 3 >> +#define BMG160_RANGE_125DPS 4 >> + >> +#define BMG160_REG_PMU_BW 0x10 >> +#define BMG160_NO_FILTER 0 >> +#define BMG160_DEF_BW 100 >> + >> +#define BMG160_REG_INT_MAP_0 0x17 >> +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) >> + >> +#define BMG160_REG_INT_MAP_1 0x18 >> +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) >> + >> +#define BMG160_REG_INT_RST_LATCH 0x21 >> +#define BMG160_INT_MODE_LATCH_RESET 0x80 >> +#define BMG160_INT_MODE_LATCH_INT 0x0F >> +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 >> + >> +#define BMG160_REG_INT_EN_0 0x15 >> +#define BMG160_DATA_ENABLE_INT BIT(7) >> + >> +#define BMG160_REG_XOUT_L 0x02 >> +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) >> + >> +#define BMG160_REG_SLOPE_THRES 0x1B >> +#define BMG160_SLOPE_THRES_MASK 0x0F >> + >> +#define BMG160_REG_MOTION_INTR 0x1C >> +#define BMG160_INT_MOTION_X BIT(0) >> +#define BMG160_INT_MOTION_Y BIT(1) >> +#define BMG160_INT_MOTION_Z BIT(2) >> +#define BMG160_ANY_DUR_MASK 0x30 >> +#define BMG160_ANY_DUR_SHIFT 4 >> + >> +#define BMG160_REG_INT_STATUS_2 0x0B >> +#define BMG160_ANY_MOTION_MASK 0x07 >> + >> +#define BMG160_REG_TEMP 0x08 >> +#define BMG160_TEMP_CENTER_VAL 23 >> + >> +#define BMG160_MAX_STARTUP_TIME_MS 80 >> + >> +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 >> + >> +struct bmg160_data { >> + struct i2c_client *client; >> + struct iio_trigger *dready_trig; >> + struct iio_trigger *motion_trig; >> + struct mutex mutex; >> + s16 buffer[8]; >> + u8 bw_bits; >> + u32 dps_range; >> + int ev_enable_state; >> + int slope_thres; >> + bool dready_trigger_on; >> + bool motion_trigger_on; >> + int64_t timestamp; >> +}; >> + >> +enum bmg160_axis { >> + AXIS_X, >> + AXIS_Y, >> + AXIS_Z, >> +}; >> + >> +static const struct { >> + int val; >> + int bw_bits; >> +} bmg160_samp_freq_table[] = { {100, 0x07}, >> + {200, 0x06}, >> + {400, 0x03}, >> + {1000, 0x02}, >> + {2000, 0x01} }; >> + >> +static const struct { >> + int scale; >> + int dps_range; >> +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, >> + { 532, BMG160_RANGE_1000DPS}, >> + { 266, BMG160_RANGE_500DPS}, >> + { 133, BMG160_RANGE_250DPS}, >> + { 66, BMG160_RANGE_125DPS} }; >> + >> +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) >> +{ >> + int ret; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_PMU_LPW, mode); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_convert_freq_to_bit(int val) >> +{ >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >> + if (bmg160_samp_freq_table[i].val == val) >> + return bmg160_samp_freq_table[i].bw_bits; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_set_bw(struct bmg160_data *data, int val) >> +{ >> + int ret; >> + int bw_bits; >> + >> + bw_bits = bmg160_convert_freq_to_bit(val); >> + if (bw_bits < 0) >> + return bw_bits; >> + >> + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, >> + bw_bits); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); >> + return ret; >> + } >> + >> + data->bw_bits = bw_bits; >> + >> + return 0; >> +} >> + >> +static int bmg160_chip_init(struct bmg160_data *data) >> +{ >> + int ret; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); >> + return ret; >> + } >> + >> + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); >> + if (ret != BMG160_CHIP_ID_VAL) { >> + dev_err(&data->client->dev, "invalid chip %x\n", ret); >> + return -ENODEV; >> + } >> + >> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + if (ret < 0) >> + return ret; >> + >> + /* Wait upto 500 ms to be ready after changing mode */ >> + usleep_range(500, 1000); >> + >> + /* Set Bandwidth */ >> + ret = bmg160_set_bw(data, BMG160_DEF_BW); >> + if (ret < 0) >> + return ret; >> + >> + /* Set Default Range */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_RANGE, >> + BMG160_RANGE_500DPS); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_range\n"); >> + return ret; >> + } >> + data->dps_range = BMG160_RANGE_500DPS; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); >> + return ret; >> + } >> + data->slope_thres = ret; >> + >> + /* Set default interrupt mode */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_motion_intr\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_set_power_state(struct bmg160_data *data, bool on) >> +{ >> + int ret; >> + >> + if (on) >> + ret = pm_runtime_get_sync(&data->client->dev); >> + else { >> + pm_runtime_mark_last_busy(&data->client->dev); >> + ret = pm_runtime_put_autosuspend(&data->client->dev); >> + } >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Failed: bmg160_set_power_state for %d\n", on); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, >> + bool status) >> +{ >> + int ret; >> + >> + /* Enable/Disable INT_MAP0 mapping */ >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); >> + return ret; >> + } >> + if (status) >> + ret |= BMG160_INT_MAP_0_BIT_ANY; >> + else >> + ret &= ~BMG160_INT_MAP_0_BIT_ANY; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_MAP_0, >> + ret); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); >> + return ret; >> + } >> + >> + /* Enable/Disable slope interrupts */ >> + if (status) { >> + /* Update slope thres */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_SLOPE_THRES, >> + data->slope_thres); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_slope_thres\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_MOTION_INTR, >> + BMG160_INT_MOTION_X | >> + BMG160_INT_MOTION_Y | >> + BMG160_INT_MOTION_Z); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_motion_intr\n"); >> + return ret; >> + } >> + >> + /* >> + * New data interrupt is always non-latched, >> + * which will have higher priority, so no need >> + * to set latched mode, we will be flooded anyway with INTR >> + */ >> + if (!data->dready_trigger_on) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + BMG160_DATA_ENABLE_INT); >> + >> + } else >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + 0); >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, >> + bool status) >> +{ >> + int ret; >> + >> + /* Enable/Disable INT_MAP1 mapping */ >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); >> + return ret; >> + } >> + >> + if (status) >> + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; >> + else >> + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_MAP_1, >> + ret); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); >> + return ret; >> + } >> + >> + if (status) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_NON_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + BMG160_DATA_ENABLE_INT); >> + >> + } else { >> + /* Restore interrupt mode */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + 0); >> + } >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_get_bw(struct bmg160_data *data, int *val) >> +{ >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >> + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { >> + *val = bmg160_samp_freq_table[i].val; >> + return IIO_VAL_INT; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_set_scale(struct bmg160_data *data, int val) >> +{ >> + int ret, i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >> + if (bmg160_scale_table[i].scale == val) { >> + ret = i2c_smbus_write_byte_data( >> + data->client, >> + BMG160_REG_RANGE, >> + bmg160_scale_table[i].dps_range); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_range\n"); >> + return ret; >> + } >> + data->dps_range = bmg160_scale_table[i].dps_range; >> + return 0; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_get_temp(struct bmg160_data *data, int *val) >> +{ >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_temp\n"); >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + *val = sign_extend32(ret, 7); >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + if (ret < 0) >> + return ret; >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) >> +{ >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading axis %d\n", axis); >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + *val = sign_extend32(ret, 15); >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + if (ret < 0) >> + return ret; >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + switch (chan->type) { >> + case IIO_TEMP: >> + return bmg160_get_temp(data, val); >> + case IIO_ANGL_VEL: >> + if (iio_buffer_enabled(indio_dev)) >> + return -EBUSY; >> + else >> + return bmg160_get_axis(data, chan->scan_index, >> + val); >> + default: >> + return -EINVAL; >> + } >> + case IIO_CHAN_INFO_OFFSET: >> + if (chan->type == IIO_TEMP) { >> + *val = BMG160_TEMP_CENTER_VAL; >> + return IIO_VAL_INT; >> + } else >> + return -EINVAL; >> + case IIO_CHAN_INFO_SCALE: >> + *val = 0; >> + switch (chan->type) { >> + case IIO_TEMP: >> + *val2 = 500000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + case IIO_ANGL_VEL: >> + { >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >> + if (bmg160_scale_table[i].dps_range == >> + data->dps_range) { >> + *val2 = bmg160_scale_table[i].scale; >> + return IIO_VAL_INT_PLUS_MICRO; >> + } >> + } >> + return -EINVAL; >> + } >> + default: >> + return -EINVAL; >> + } >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + *val2 = 0; >> + mutex_lock(&data->mutex); >> + ret = bmg160_get_bw(data, val); >> + mutex_unlock(&data->mutex); >> + return ret; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static int bmg160_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int val, int val2, long mask) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + mutex_lock(&data->mutex); >> + /* >> + * Section 4.2 of spec >> + * In suspend mode, the only supported operations are reading >> + * registers as well as writing to the (0x14) softreset >> + * register. Since we will be in suspend mode by default, change >> + * mode to power on for other writes. >> + */ >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_bw(data, val); >> + if (ret < 0) { >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_power_state(data, false); > Could this end up disabling the part when in triggered mode? >> + mutex_unlock(&data->mutex); >> + return ret; >> + case IIO_CHAN_INFO_SCALE: >> + if (val) >> + return -EINVAL; >> + >> + mutex_lock(&data->mutex); >> + /* Refer to comments above for the suspend mode ops */ >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_scale(data, val2); >> + if (ret < 0) { >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + default: >> + return -EINVAL; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_read_event(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + enum iio_event_info info, >> + int *val, int *val2) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + *val2 = 0; >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_write_event(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + enum iio_event_info info, >> + int val, int val2) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + if (data->ev_enable_state) >> + return -EBUSY; >> + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; >> + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_read_event_config(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir) >> +{ >> + >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + return data->ev_enable_state; >> +} >> + >> +static int bmg160_write_event_config(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + int state) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + if (state && data->ev_enable_state) >> + return 0; >> + >> + mutex_lock(&data->mutex); >> + >> + if (!state && data->motion_trigger_on) { >> + data->ev_enable_state = 0; >> + mutex_unlock(&data->mutex); >> + return 0; >> + } >> + /* >> + * We will expect the enable and disable to do operation in >> + * in reverse order. This will happen here anyway as our >> + * resume operation uses sync mode runtime pm calls, the >> + * suspend operation will be delayed by autosuspend delay >> + * So the disable operation will still happen in reverse of >> + * enable operation. When runtime pm is disabled the mode >> + * is always on so sequence doesn't matter >> + */ > Thanks for the detailed comment! >> + ret = bmg160_set_power_state(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = bmg160_setup_any_motion_interrupt(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + data->ev_enable_state = state; >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static int bmg160_validate_trigger(struct iio_dev *indio_dev, >> + struct iio_trigger *trig) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + if (data->dready_trig != trig && data->motion_trig != trig) >> + return -EINVAL; >> + >> + return 0; >> +} >> + >> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); >> + >> +static IIO_CONST_ATTR(in_anglvel_scale_available, >> + "0.001065 0.000532 0.000266 0.000133 0.000066"); >> + >> +static struct attribute *bmg160_attributes[] = { >> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, >> + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, >> + NULL, >> +}; >> + >> +static const struct attribute_group bmg160_attrs_group = { >> + .attrs = bmg160_attributes, >> +}; >> + >> +static const struct iio_event_spec bmg160_event = { >> + .type = IIO_EV_TYPE_ROC, >> + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, >> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | >> + BIT(IIO_EV_INFO_ENABLE) >> +}; >> + >> +#define BMG160_CHANNEL(_axis) { \ >> + .type = IIO_ANGL_VEL, \ >> + .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_index = AXIS_##_axis, \ >> + .scan_type = { \ >> + .sign = 's', \ >> + .realbits = 16, \ >> + .storagebits = 16, \ >> + }, \ >> + .event_spec = &bmg160_event, \ >> + .num_event_specs = 1 \ >> +} >> + >> +static const struct iio_chan_spec bmg160_channels[] = { >> + { >> + .type = IIO_TEMP, >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >> + BIT(IIO_CHAN_INFO_SCALE) | >> + BIT(IIO_CHAN_INFO_OFFSET), >> + .scan_index = -1, >> + }, >> + BMG160_CHANNEL(X), >> + BMG160_CHANNEL(Y), >> + BMG160_CHANNEL(Z), >> + IIO_CHAN_SOFT_TIMESTAMP(3), >> +}; >> + >> +static const struct iio_info bmg160_info = { >> + .attrs = &bmg160_attrs_group, >> + .read_raw = bmg160_read_raw, >> + .write_raw = bmg160_write_raw, >> + .read_event_value = bmg160_read_event, >> + .write_event_value = bmg160_write_event, >> + .write_event_config = bmg160_write_event_config, >> + .read_event_config = bmg160_read_event_config, >> + .validate_trigger = bmg160_validate_trigger, >> + .driver_module = THIS_MODULE, >> +}; >> + >> +static irqreturn_t bmg160_trigger_handler(int irq, void *p) >> +{ >> + struct iio_poll_func *pf = p; >> + struct iio_dev *indio_dev = pf->indio_dev; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int bit, ret, i = 0; >> + >> + mutex_lock(&data->mutex); >> + for_each_set_bit(bit, indio_dev->buffer->scan_mask, >> + indio_dev->masklength) { >> + ret = i2c_smbus_read_word_data(data->client, >> + BMG160_AXIS_TO_REG(bit)); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + goto err; >> + } >> + data->buffer[i++] = ret; >> + } >> + mutex_unlock(&data->mutex); >> + >> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, >> + data->timestamp); >> +err: >> + iio_trigger_notify_done(indio_dev->trig); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int bmg160_trig_try_reen(struct iio_trigger *trig) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + /* new data interrupts don't need ack */ >> + if (data->dready_trigger_on) >> + return 0; >> + >> + /* Set latched mode interrupt and clear any latched interrupt */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, >> + bool state) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + >> + if (!state && data->ev_enable_state && data->motion_trigger_on) { >> + data->motion_trigger_on = false; >> + mutex_unlock(&data->mutex); >> + return 0; >> + } >> + >> + /* >> + * Refer to comment in bmg160_write_event_config for >> + * enable/disable operation order >> + */ >> + ret = bmg160_set_power_state(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + if (data->motion_trig == trig) >> + ret = bmg160_setup_any_motion_interrupt(data, state); >> + else >> + ret = bmg160_setup_new_data_interrupt(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + if (data->motion_trig == trig) >> + data->motion_trigger_on = state; >> + else >> + data->dready_trigger_on = state; >> + >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static const struct iio_trigger_ops bmg160_trigger_ops = { >> + .set_trigger_state = bmg160_data_rdy_trigger_set_state, >> + .try_reenable = bmg160_trig_try_reen, >> + .owner = THIS_MODULE, >> +}; >> + >> +static irqreturn_t bmg160_event_handler(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + int dir; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); >> + goto ack_intr_status; >> + } >> + >> + if (ret & 0x08) >> + dir = IIO_EV_DIR_RISING; >> + else >> + dir = IIO_EV_DIR_FALLING; >> + >> + if (ret & BMG160_ANY_MOTION_MASK) >> + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, >> + 0, >> + IIO_MOD_X_OR_Y_OR_Z, >> + IIO_EV_TYPE_ROC, >> + dir), >> + data->timestamp); >> + >> +ack_intr_status: >> + if (!data->dready_trigger_on) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + } >> + >> + return IRQ_HANDLED; >> +} >> + >> +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + data->timestamp = iio_get_time_ns(); >> + >> + if (data->dready_trigger_on) >> + iio_trigger_poll(data->dready_trig); >> + else if (data->motion_trigger_on) >> + iio_trigger_poll(data->motion_trig); >> + >> + if (data->ev_enable_state) >> + return IRQ_WAKE_THREAD; >> + else >> + return IRQ_HANDLED; >> + >> +} >> + >> +static int bmg160_acpi_gpio_probe(struct i2c_client *client, >> + struct bmg160_data *data) >> +{ >> + const struct acpi_device_id *id; >> + struct device *dev; >> + struct gpio_desc *gpio; >> + int ret; >> + >> + if (!client) >> + return -EINVAL; >> + >> + dev = &client->dev; >> + if (!ACPI_HANDLE(dev)) >> + return -ENODEV; >> + >> + id = acpi_match_device(dev->driver->acpi_match_table, dev); >> + if (!id) >> + return -ENODEV; >> + >> + /* data ready gpio interrupt pin */ >> + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); >> + if (IS_ERR(gpio)) { >> + dev_err(dev, "acpi gpio get index failed\n"); >> + return PTR_ERR(gpio); >> + } >> + >> + ret = gpiod_direction_input(gpio); >> + if (ret) >> + return ret; >> + >> + ret = gpiod_to_irq(gpio); >> + >> + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); >> + >> + return ret; >> +} >> + >> +static int bmg160_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct bmg160_data *data; >> + struct iio_dev *indio_dev; >> + int ret; >> + >> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + data = iio_priv(indio_dev); >> + i2c_set_clientdata(client, indio_dev); >> + data->client = client; >> + >> + ret = bmg160_chip_init(data); >> + if (ret < 0) >> + return ret; >> + >> + mutex_init(&data->mutex); >> + >> + indio_dev->dev.parent = &client->dev; >> + indio_dev->channels = bmg160_channels; >> + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); >> + indio_dev->name = BMG160_DRV_NAME; >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->info = &bmg160_info; >> + >> + if (client->irq <= 0) >> + client->irq = bmg160_acpi_gpio_probe(client, data); >> + >> + if (client->irq > 0) { >> + ret = devm_request_threaded_irq(&client->dev, >> + client->irq, >> + bmg160_data_rdy_trig_poll, >> + bmg160_event_handler, >> + IRQF_TRIGGER_RISING, >> + BMG160_IRQ_NAME, >> + indio_dev); >> + if (ret) >> + return ret; >> + >> + data->dready_trig = devm_iio_trigger_alloc(&client->dev, >> + "%s-dev%d", >> + indio_dev->name, >> + indio_dev->id); >> + if (!data->dready_trig) >> + return -ENOMEM; >> + >> + data->motion_trig = devm_iio_trigger_alloc(&client->dev, >> + "%s-any-motion-dev%d", >> + indio_dev->name, >> + indio_dev->id); >> + if (!data->motion_trig) >> + return -ENOMEM; >> + >> + data->dready_trig->dev.parent = &client->dev; >> + data->dready_trig->ops = &bmg160_trigger_ops; >> + iio_trigger_set_drvdata(data->dready_trig, indio_dev); >> + ret = iio_trigger_register(data->dready_trig); >> + if (ret) >> + return ret; >> + >> + data->motion_trig->dev.parent = &client->dev; >> + data->motion_trig->ops = &bmg160_trigger_ops; >> + iio_trigger_set_drvdata(data->motion_trig, indio_dev); >> + ret = iio_trigger_register(data->motion_trig); >> + if (ret) { >> + data->motion_trig = NULL; >> + goto err_trigger_unregister; >> + } >> + >> + ret = iio_triggered_buffer_setup(indio_dev, >> + NULL, >> + bmg160_trigger_handler, >> + NULL); >> + if (ret < 0) { >> + dev_err(&client->dev, >> + "iio triggered buffer setup failed\n"); >> + goto err_trigger_unregister; >> + } >> + } >> + >> + ret = iio_device_register(indio_dev); >> + if (ret < 0) { >> + dev_err(&client->dev, "unable to register iio device\n"); >> + goto err_buffer_cleanup; >> + } >> + >> + ret = pm_runtime_set_active(&client->dev); >> + if (ret) >> + goto err_iio_unregister; >> + >> + pm_runtime_enable(&client->dev); >> + pm_runtime_set_autosuspend_delay(&client->dev, >> + BMG160_AUTO_SUSPEND_DELAY_MS); >> + pm_runtime_use_autosuspend(&client->dev); >> + >> + return 0; >> + >> +err_iio_unregister: >> + iio_device_unregister(indio_dev); >> +err_buffer_cleanup: >> + if (data->dready_trig) >> + iio_triggered_buffer_cleanup(indio_dev); >> +err_trigger_unregister: >> + if (data->dready_trig) >> + iio_trigger_unregister(data->dready_trig); >> + if (data->motion_trig) >> + iio_trigger_unregister(data->motion_trig); >> + >> + return ret; >> +} >> + >> +static int bmg160_remove(struct i2c_client *client) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + pm_runtime_disable(&client->dev); >> + pm_runtime_set_suspended(&client->dev); >> + pm_runtime_put_noidle(&client->dev); >> + >> + iio_device_unregister(indio_dev); >> + >> + if (data->dready_trig) { >> + iio_triggered_buffer_cleanup(indio_dev); >> + iio_trigger_unregister(data->dready_trig); >> + iio_trigger_unregister(data->motion_trig); >> + } >> + >> + mutex_lock(&data->mutex); >> + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +#ifdef CONFIG_PM_SLEEP >> +static int bmg160_suspend(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->mutex); >> + bmg160_set_mode(data, BMG160_MODE_SUSPEND); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static int bmg160_resume(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->mutex); >> + if (data->dready_trigger_on || data->motion_trigger_on || >> + data->ev_enable_state) >> + bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> +#endif >> + >> +#ifdef CONFIG_PM_RUNTIME >> +static int bmg160_runtime_suspend(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); >> +} >> + >> +static int bmg160_runtime_resume(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + if (ret < 0) >> + return ret; >> + >> + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); >> + >> + return 0; >> +} >> +#endif >> + >> +static const struct dev_pm_ops bmg160_pm_ops = { >> + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) >> + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, >> + bmg160_runtime_resume, NULL) >> +}; >> + >> +static const struct acpi_device_id bmg160_acpi_match[] = { >> + {"BMG0160", 0}, >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); >> + >> +static const struct i2c_device_id bmg160_id[] = { >> + {"bmg160", 0}, >> + {} >> +}; >> + >> +MODULE_DEVICE_TABLE(i2c, bmg160_id); >> + >> +static struct i2c_driver bmg160_driver = { >> + .driver = { >> + .name = BMG160_DRV_NAME, >> + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), >> + .pm = &bmg160_pm_ops, >> + }, >> + .probe = bmg160_probe, >> + .remove = bmg160_remove, >> + .id_table = bmg160_id, >> +}; >> +module_i2c_driver(bmg160_driver); >> + >> +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_DESCRIPTION("BMG160 Gyro driver"); >> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-07 10:39 ` Jonathan Cameron 2014-08-08 2:47 ` Srinivas Pandruvada @ 2014-08-12 19:06 ` Srinivas Pandruvada 2014-08-19 19:51 ` Jonathan Cameron 1 sibling, 1 reply; 8+ messages in thread From: Srinivas Pandruvada @ 2014-08-12 19:06 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio On 08/07/2014 03:39 AM, Jonathan Cameron wrote: > On 05/08/14 22:57, Srinivas Pandruvada wrote: >> This change implements support for BMG160 Gyro sensor. Although chip >> has several advanced features, this change implements minimum set >> required for using gyro sensor. >> >> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> > One more query. In the write_raw function you always enable then suspend the > device. The enable is safe, but if you have a write to the attributes > whilst operating in triggered / buffered mode will it not disable the > device? It is perfectly permissible to ignore such a write (return -EBUSY) > but I don't think you are doing so. > I tested this. When trigger/events are active then first call to bmg160_set_power_state will increment runtime pm refcount and also execute runtime resume to change the mode to NORMAL. But when write_raw is called while the trigger/events are active the runtime PM refcount is incremented since it was already more than 0, so the resume is not actually called. When at the end of write_raw bmg160_set_power_state to turn off, the ref count is simply decremented, but since it is not zero, runtime suspend will not be called. Thanks, Srinivas > Otherwise, looks good to me. Nice explanatory comments as well! >> --- >> drivers/iio/gyro/Kconfig | 11 + >> drivers/iio/gyro/Makefile | 1 + >> drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 1223 insertions(+) >> create mode 100644 drivers/iio/gyro/bmg160.c >> >> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig >> index ac2d69e..d630ae9 100644 >> --- a/drivers/iio/gyro/Kconfig >> +++ b/drivers/iio/gyro/Kconfig >> @@ -50,6 +50,17 @@ config ADXRS450 >> This driver can also be built as a module. If so, the module >> will be called adxrs450. >> >> +config BMG160 >> + tristate "BOSCH BMG160 Gyro Sensor" >> + depends on I2C >> + select IIO_TRIGGERED_BUFFER if IIO_BUFFER >> + help >> + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor >> + driver. >> + >> + This driver can also be built as a module. If so, the module >> + will be called bmg160. >> + >> config HID_SENSOR_GYRO_3D >> depends on HID_SENSOR_HUB >> select IIO_BUFFER >> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile >> index 2f2752a..36a3877 100644 >> --- a/drivers/iio/gyro/Makefile >> +++ b/drivers/iio/gyro/Makefile >> @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o >> obj-$(CONFIG_ADIS16136) += adis16136.o >> obj-$(CONFIG_ADIS16260) += adis16260.o >> obj-$(CONFIG_ADXRS450) += adxrs450.o >> +obj-$(CONFIG_BMG160) += bmg160.o >> >> obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o >> >> diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c >> new file mode 100644 >> index 0000000..80f92a6 >> --- /dev/null >> +++ b/drivers/iio/gyro/bmg160.c >> @@ -0,0 +1,1211 @@ >> +/* >> + * BMG160 Gyro Sensor driver >> + * Copyright (c) 2014, Intel Corporation. >> + * >> + * This program is free software; you can redistribute it and/or modify it >> + * under the terms and conditions of the GNU General Public License, >> + * version 2, as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope it will be useful, but WITHOUT >> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for >> + * more details. >> + */ >> + >> +#include <linux/module.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/delay.h> >> +#include <linux/slab.h> >> +#include <linux/acpi.h> >> +#include <linux/gpio/consumer.h> >> +#include <linux/pm.h> >> +#include <linux/pm_runtime.h> >> +#include <linux/iio/iio.h> >> +#include <linux/iio/sysfs.h> >> +#include <linux/iio/buffer.h> >> +#include <linux/iio/trigger.h> >> +#include <linux/iio/events.h> >> +#include <linux/iio/trigger_consumer.h> >> +#include <linux/iio/triggered_buffer.h> >> + >> +#define BMG160_DRV_NAME "bmg160" >> +#define BMG160_IRQ_NAME "bmg160_event" >> +#define BMG160_GPIO_NAME "gpio_int" >> + >> +#define BMG160_REG_CHIP_ID 0x00 >> +#define BMG160_CHIP_ID_VAL 0x0F >> + >> +#define BMG160_REG_PMU_LPW 0x11 >> +#define BMG160_MODE_NORMAL 0x00 >> +#define BMG160_MODE_DEEP_SUSPEND 0x20 >> +#define BMG160_MODE_SUSPEND 0x80 >> + >> +#define BMG160_REG_RANGE 0x0F >> + >> +#define BMG160_RANGE_2000DPS 0 >> +#define BMG160_RANGE_1000DPS 1 >> +#define BMG160_RANGE_500DPS 2 >> +#define BMG160_RANGE_250DPS 3 >> +#define BMG160_RANGE_125DPS 4 >> + >> +#define BMG160_REG_PMU_BW 0x10 >> +#define BMG160_NO_FILTER 0 >> +#define BMG160_DEF_BW 100 >> + >> +#define BMG160_REG_INT_MAP_0 0x17 >> +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) >> + >> +#define BMG160_REG_INT_MAP_1 0x18 >> +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) >> + >> +#define BMG160_REG_INT_RST_LATCH 0x21 >> +#define BMG160_INT_MODE_LATCH_RESET 0x80 >> +#define BMG160_INT_MODE_LATCH_INT 0x0F >> +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 >> + >> +#define BMG160_REG_INT_EN_0 0x15 >> +#define BMG160_DATA_ENABLE_INT BIT(7) >> + >> +#define BMG160_REG_XOUT_L 0x02 >> +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) >> + >> +#define BMG160_REG_SLOPE_THRES 0x1B >> +#define BMG160_SLOPE_THRES_MASK 0x0F >> + >> +#define BMG160_REG_MOTION_INTR 0x1C >> +#define BMG160_INT_MOTION_X BIT(0) >> +#define BMG160_INT_MOTION_Y BIT(1) >> +#define BMG160_INT_MOTION_Z BIT(2) >> +#define BMG160_ANY_DUR_MASK 0x30 >> +#define BMG160_ANY_DUR_SHIFT 4 >> + >> +#define BMG160_REG_INT_STATUS_2 0x0B >> +#define BMG160_ANY_MOTION_MASK 0x07 >> + >> +#define BMG160_REG_TEMP 0x08 >> +#define BMG160_TEMP_CENTER_VAL 23 >> + >> +#define BMG160_MAX_STARTUP_TIME_MS 80 >> + >> +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 >> + >> +struct bmg160_data { >> + struct i2c_client *client; >> + struct iio_trigger *dready_trig; >> + struct iio_trigger *motion_trig; >> + struct mutex mutex; >> + s16 buffer[8]; >> + u8 bw_bits; >> + u32 dps_range; >> + int ev_enable_state; >> + int slope_thres; >> + bool dready_trigger_on; >> + bool motion_trigger_on; >> + int64_t timestamp; >> +}; >> + >> +enum bmg160_axis { >> + AXIS_X, >> + AXIS_Y, >> + AXIS_Z, >> +}; >> + >> +static const struct { >> + int val; >> + int bw_bits; >> +} bmg160_samp_freq_table[] = { {100, 0x07}, >> + {200, 0x06}, >> + {400, 0x03}, >> + {1000, 0x02}, >> + {2000, 0x01} }; >> + >> +static const struct { >> + int scale; >> + int dps_range; >> +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, >> + { 532, BMG160_RANGE_1000DPS}, >> + { 266, BMG160_RANGE_500DPS}, >> + { 133, BMG160_RANGE_250DPS}, >> + { 66, BMG160_RANGE_125DPS} }; >> + >> +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) >> +{ >> + int ret; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_PMU_LPW, mode); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_convert_freq_to_bit(int val) >> +{ >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >> + if (bmg160_samp_freq_table[i].val == val) >> + return bmg160_samp_freq_table[i].bw_bits; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_set_bw(struct bmg160_data *data, int val) >> +{ >> + int ret; >> + int bw_bits; >> + >> + bw_bits = bmg160_convert_freq_to_bit(val); >> + if (bw_bits < 0) >> + return bw_bits; >> + >> + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, >> + bw_bits); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); >> + return ret; >> + } >> + >> + data->bw_bits = bw_bits; >> + >> + return 0; >> +} >> + >> +static int bmg160_chip_init(struct bmg160_data *data) >> +{ >> + int ret; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); >> + return ret; >> + } >> + >> + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); >> + if (ret != BMG160_CHIP_ID_VAL) { >> + dev_err(&data->client->dev, "invalid chip %x\n", ret); >> + return -ENODEV; >> + } >> + >> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + if (ret < 0) >> + return ret; >> + >> + /* Wait upto 500 ms to be ready after changing mode */ >> + usleep_range(500, 1000); >> + >> + /* Set Bandwidth */ >> + ret = bmg160_set_bw(data, BMG160_DEF_BW); >> + if (ret < 0) >> + return ret; >> + >> + /* Set Default Range */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_RANGE, >> + BMG160_RANGE_500DPS); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_range\n"); >> + return ret; >> + } >> + data->dps_range = BMG160_RANGE_500DPS; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); >> + return ret; >> + } >> + data->slope_thres = ret; >> + >> + /* Set default interrupt mode */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_motion_intr\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_set_power_state(struct bmg160_data *data, bool on) >> +{ >> + int ret; >> + >> + if (on) >> + ret = pm_runtime_get_sync(&data->client->dev); >> + else { >> + pm_runtime_mark_last_busy(&data->client->dev); >> + ret = pm_runtime_put_autosuspend(&data->client->dev); >> + } >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Failed: bmg160_set_power_state for %d\n", on); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, >> + bool status) >> +{ >> + int ret; >> + >> + /* Enable/Disable INT_MAP0 mapping */ >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); >> + return ret; >> + } >> + if (status) >> + ret |= BMG160_INT_MAP_0_BIT_ANY; >> + else >> + ret &= ~BMG160_INT_MAP_0_BIT_ANY; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_MAP_0, >> + ret); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); >> + return ret; >> + } >> + >> + /* Enable/Disable slope interrupts */ >> + if (status) { >> + /* Update slope thres */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_SLOPE_THRES, >> + data->slope_thres); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_slope_thres\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_MOTION_INTR, >> + BMG160_INT_MOTION_X | >> + BMG160_INT_MOTION_Y | >> + BMG160_INT_MOTION_Z); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_motion_intr\n"); >> + return ret; >> + } >> + >> + /* >> + * New data interrupt is always non-latched, >> + * which will have higher priority, so no need >> + * to set latched mode, we will be flooded anyway with INTR >> + */ >> + if (!data->dready_trigger_on) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + BMG160_DATA_ENABLE_INT); >> + >> + } else >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + 0); >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, >> + bool status) >> +{ >> + int ret; >> + >> + /* Enable/Disable INT_MAP1 mapping */ >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); >> + return ret; >> + } >> + >> + if (status) >> + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; >> + else >> + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_MAP_1, >> + ret); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); >> + return ret; >> + } >> + >> + if (status) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_NON_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + BMG160_DATA_ENABLE_INT); >> + >> + } else { >> + /* Restore interrupt mode */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_EN_0, >> + 0); >> + } >> + >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_get_bw(struct bmg160_data *data, int *val) >> +{ >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >> + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { >> + *val = bmg160_samp_freq_table[i].val; >> + return IIO_VAL_INT; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_set_scale(struct bmg160_data *data, int val) >> +{ >> + int ret, i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >> + if (bmg160_scale_table[i].scale == val) { >> + ret = i2c_smbus_write_byte_data( >> + data->client, >> + BMG160_REG_RANGE, >> + bmg160_scale_table[i].dps_range); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Error writing reg_range\n"); >> + return ret; >> + } >> + data->dps_range = bmg160_scale_table[i].dps_range; >> + return 0; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_get_temp(struct bmg160_data *data, int *val) >> +{ >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_temp\n"); >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + *val = sign_extend32(ret, 7); >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + if (ret < 0) >> + return ret; >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) >> +{ >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading axis %d\n", axis); >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + *val = sign_extend32(ret, 15); >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + if (ret < 0) >> + return ret; >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + switch (chan->type) { >> + case IIO_TEMP: >> + return bmg160_get_temp(data, val); >> + case IIO_ANGL_VEL: >> + if (iio_buffer_enabled(indio_dev)) >> + return -EBUSY; >> + else >> + return bmg160_get_axis(data, chan->scan_index, >> + val); >> + default: >> + return -EINVAL; >> + } >> + case IIO_CHAN_INFO_OFFSET: >> + if (chan->type == IIO_TEMP) { >> + *val = BMG160_TEMP_CENTER_VAL; >> + return IIO_VAL_INT; >> + } else >> + return -EINVAL; >> + case IIO_CHAN_INFO_SCALE: >> + *val = 0; >> + switch (chan->type) { >> + case IIO_TEMP: >> + *val2 = 500000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + case IIO_ANGL_VEL: >> + { >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >> + if (bmg160_scale_table[i].dps_range == >> + data->dps_range) { >> + *val2 = bmg160_scale_table[i].scale; >> + return IIO_VAL_INT_PLUS_MICRO; >> + } >> + } >> + return -EINVAL; >> + } >> + default: >> + return -EINVAL; >> + } >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + *val2 = 0; >> + mutex_lock(&data->mutex); >> + ret = bmg160_get_bw(data, val); >> + mutex_unlock(&data->mutex); >> + return ret; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static int bmg160_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int val, int val2, long mask) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_SAMP_FREQ: >> + mutex_lock(&data->mutex); >> + /* >> + * Section 4.2 of spec >> + * In suspend mode, the only supported operations are reading >> + * registers as well as writing to the (0x14) softreset >> + * register. Since we will be in suspend mode by default, change >> + * mode to power on for other writes. >> + */ >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_bw(data, val); >> + if (ret < 0) { >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_power_state(data, false); > Could this end up disabling the part when in triggered mode? >> + mutex_unlock(&data->mutex); >> + return ret; >> + case IIO_CHAN_INFO_SCALE: >> + if (val) >> + return -EINVAL; >> + >> + mutex_lock(&data->mutex); >> + /* Refer to comments above for the suspend mode ops */ >> + ret = bmg160_set_power_state(data, true); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_scale(data, val2); >> + if (ret < 0) { >> + bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + ret = bmg160_set_power_state(data, false); >> + mutex_unlock(&data->mutex); >> + return ret; >> + default: >> + return -EINVAL; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int bmg160_read_event(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + enum iio_event_info info, >> + int *val, int *val2) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + *val2 = 0; >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return IIO_VAL_INT; >> +} >> + >> +static int bmg160_write_event(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + enum iio_event_info info, >> + int val, int val2) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + if (data->ev_enable_state) >> + return -EBUSY; >> + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; >> + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_read_event_config(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir) >> +{ >> + >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + return data->ev_enable_state; >> +} >> + >> +static int bmg160_write_event_config(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, >> + enum iio_event_type type, >> + enum iio_event_direction dir, >> + int state) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + if (state && data->ev_enable_state) >> + return 0; >> + >> + mutex_lock(&data->mutex); >> + >> + if (!state && data->motion_trigger_on) { >> + data->ev_enable_state = 0; >> + mutex_unlock(&data->mutex); >> + return 0; >> + } >> + /* >> + * We will expect the enable and disable to do operation in >> + * in reverse order. This will happen here anyway as our >> + * resume operation uses sync mode runtime pm calls, the >> + * suspend operation will be delayed by autosuspend delay >> + * So the disable operation will still happen in reverse of >> + * enable operation. When runtime pm is disabled the mode >> + * is always on so sequence doesn't matter >> + */ > Thanks for the detailed comment! >> + ret = bmg160_set_power_state(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + ret = bmg160_setup_any_motion_interrupt(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + >> + data->ev_enable_state = state; >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static int bmg160_validate_trigger(struct iio_dev *indio_dev, >> + struct iio_trigger *trig) >> +{ >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + if (data->dready_trig != trig && data->motion_trig != trig) >> + return -EINVAL; >> + >> + return 0; >> +} >> + >> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); >> + >> +static IIO_CONST_ATTR(in_anglvel_scale_available, >> + "0.001065 0.000532 0.000266 0.000133 0.000066"); >> + >> +static struct attribute *bmg160_attributes[] = { >> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, >> + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, >> + NULL, >> +}; >> + >> +static const struct attribute_group bmg160_attrs_group = { >> + .attrs = bmg160_attributes, >> +}; >> + >> +static const struct iio_event_spec bmg160_event = { >> + .type = IIO_EV_TYPE_ROC, >> + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, >> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | >> + BIT(IIO_EV_INFO_ENABLE) >> +}; >> + >> +#define BMG160_CHANNEL(_axis) { \ >> + .type = IIO_ANGL_VEL, \ >> + .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_index = AXIS_##_axis, \ >> + .scan_type = { \ >> + .sign = 's', \ >> + .realbits = 16, \ >> + .storagebits = 16, \ >> + }, \ >> + .event_spec = &bmg160_event, \ >> + .num_event_specs = 1 \ >> +} >> + >> +static const struct iio_chan_spec bmg160_channels[] = { >> + { >> + .type = IIO_TEMP, >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >> + BIT(IIO_CHAN_INFO_SCALE) | >> + BIT(IIO_CHAN_INFO_OFFSET), >> + .scan_index = -1, >> + }, >> + BMG160_CHANNEL(X), >> + BMG160_CHANNEL(Y), >> + BMG160_CHANNEL(Z), >> + IIO_CHAN_SOFT_TIMESTAMP(3), >> +}; >> + >> +static const struct iio_info bmg160_info = { >> + .attrs = &bmg160_attrs_group, >> + .read_raw = bmg160_read_raw, >> + .write_raw = bmg160_write_raw, >> + .read_event_value = bmg160_read_event, >> + .write_event_value = bmg160_write_event, >> + .write_event_config = bmg160_write_event_config, >> + .read_event_config = bmg160_read_event_config, >> + .validate_trigger = bmg160_validate_trigger, >> + .driver_module = THIS_MODULE, >> +}; >> + >> +static irqreturn_t bmg160_trigger_handler(int irq, void *p) >> +{ >> + struct iio_poll_func *pf = p; >> + struct iio_dev *indio_dev = pf->indio_dev; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int bit, ret, i = 0; >> + >> + mutex_lock(&data->mutex); >> + for_each_set_bit(bit, indio_dev->buffer->scan_mask, >> + indio_dev->masklength) { >> + ret = i2c_smbus_read_word_data(data->client, >> + BMG160_AXIS_TO_REG(bit)); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + goto err; >> + } >> + data->buffer[i++] = ret; >> + } >> + mutex_unlock(&data->mutex); >> + >> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, >> + data->timestamp); >> +err: >> + iio_trigger_notify_done(indio_dev->trig); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int bmg160_trig_try_reen(struct iio_trigger *trig) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + /* new data interrupts don't need ack */ >> + if (data->dready_trigger_on) >> + return 0; >> + >> + /* Set latched mode interrupt and clear any latched interrupt */ >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, >> + bool state) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + mutex_lock(&data->mutex); >> + >> + if (!state && data->ev_enable_state && data->motion_trigger_on) { >> + data->motion_trigger_on = false; >> + mutex_unlock(&data->mutex); >> + return 0; >> + } >> + >> + /* >> + * Refer to comment in bmg160_write_event_config for >> + * enable/disable operation order >> + */ >> + ret = bmg160_set_power_state(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + if (data->motion_trig == trig) >> + ret = bmg160_setup_any_motion_interrupt(data, state); >> + else >> + ret = bmg160_setup_new_data_interrupt(data, state); >> + if (ret < 0) { >> + mutex_unlock(&data->mutex); >> + return ret; >> + } >> + if (data->motion_trig == trig) >> + data->motion_trigger_on = state; >> + else >> + data->dready_trigger_on = state; >> + >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static const struct iio_trigger_ops bmg160_trigger_ops = { >> + .set_trigger_state = bmg160_data_rdy_trigger_set_state, >> + .try_reenable = bmg160_trig_try_reen, >> + .owner = THIS_MODULE, >> +}; >> + >> +static irqreturn_t bmg160_event_handler(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + int dir; >> + >> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); >> + goto ack_intr_status; >> + } >> + >> + if (ret & 0x08) >> + dir = IIO_EV_DIR_RISING; >> + else >> + dir = IIO_EV_DIR_FALLING; >> + >> + if (ret & BMG160_ANY_MOTION_MASK) >> + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, >> + 0, >> + IIO_MOD_X_OR_Y_OR_Z, >> + IIO_EV_TYPE_ROC, >> + dir), >> + data->timestamp); >> + >> +ack_intr_status: >> + if (!data->dready_trigger_on) { >> + ret = i2c_smbus_write_byte_data(data->client, >> + BMG160_REG_INT_RST_LATCH, >> + BMG160_INT_MODE_LATCH_INT | >> + BMG160_INT_MODE_LATCH_RESET); >> + if (ret < 0) >> + dev_err(&data->client->dev, >> + "Error writing reg_rst_latch\n"); >> + } >> + >> + return IRQ_HANDLED; >> +} >> + >> +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + data->timestamp = iio_get_time_ns(); >> + >> + if (data->dready_trigger_on) >> + iio_trigger_poll(data->dready_trig); >> + else if (data->motion_trigger_on) >> + iio_trigger_poll(data->motion_trig); >> + >> + if (data->ev_enable_state) >> + return IRQ_WAKE_THREAD; >> + else >> + return IRQ_HANDLED; >> + >> +} >> + >> +static int bmg160_acpi_gpio_probe(struct i2c_client *client, >> + struct bmg160_data *data) >> +{ >> + const struct acpi_device_id *id; >> + struct device *dev; >> + struct gpio_desc *gpio; >> + int ret; >> + >> + if (!client) >> + return -EINVAL; >> + >> + dev = &client->dev; >> + if (!ACPI_HANDLE(dev)) >> + return -ENODEV; >> + >> + id = acpi_match_device(dev->driver->acpi_match_table, dev); >> + if (!id) >> + return -ENODEV; >> + >> + /* data ready gpio interrupt pin */ >> + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); >> + if (IS_ERR(gpio)) { >> + dev_err(dev, "acpi gpio get index failed\n"); >> + return PTR_ERR(gpio); >> + } >> + >> + ret = gpiod_direction_input(gpio); >> + if (ret) >> + return ret; >> + >> + ret = gpiod_to_irq(gpio); >> + >> + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); >> + >> + return ret; >> +} >> + >> +static int bmg160_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct bmg160_data *data; >> + struct iio_dev *indio_dev; >> + int ret; >> + >> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + data = iio_priv(indio_dev); >> + i2c_set_clientdata(client, indio_dev); >> + data->client = client; >> + >> + ret = bmg160_chip_init(data); >> + if (ret < 0) >> + return ret; >> + >> + mutex_init(&data->mutex); >> + >> + indio_dev->dev.parent = &client->dev; >> + indio_dev->channels = bmg160_channels; >> + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); >> + indio_dev->name = BMG160_DRV_NAME; >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->info = &bmg160_info; >> + >> + if (client->irq <= 0) >> + client->irq = bmg160_acpi_gpio_probe(client, data); >> + >> + if (client->irq > 0) { >> + ret = devm_request_threaded_irq(&client->dev, >> + client->irq, >> + bmg160_data_rdy_trig_poll, >> + bmg160_event_handler, >> + IRQF_TRIGGER_RISING, >> + BMG160_IRQ_NAME, >> + indio_dev); >> + if (ret) >> + return ret; >> + >> + data->dready_trig = devm_iio_trigger_alloc(&client->dev, >> + "%s-dev%d", >> + indio_dev->name, >> + indio_dev->id); >> + if (!data->dready_trig) >> + return -ENOMEM; >> + >> + data->motion_trig = devm_iio_trigger_alloc(&client->dev, >> + "%s-any-motion-dev%d", >> + indio_dev->name, >> + indio_dev->id); >> + if (!data->motion_trig) >> + return -ENOMEM; >> + >> + data->dready_trig->dev.parent = &client->dev; >> + data->dready_trig->ops = &bmg160_trigger_ops; >> + iio_trigger_set_drvdata(data->dready_trig, indio_dev); >> + ret = iio_trigger_register(data->dready_trig); >> + if (ret) >> + return ret; >> + >> + data->motion_trig->dev.parent = &client->dev; >> + data->motion_trig->ops = &bmg160_trigger_ops; >> + iio_trigger_set_drvdata(data->motion_trig, indio_dev); >> + ret = iio_trigger_register(data->motion_trig); >> + if (ret) { >> + data->motion_trig = NULL; >> + goto err_trigger_unregister; >> + } >> + >> + ret = iio_triggered_buffer_setup(indio_dev, >> + NULL, >> + bmg160_trigger_handler, >> + NULL); >> + if (ret < 0) { >> + dev_err(&client->dev, >> + "iio triggered buffer setup failed\n"); >> + goto err_trigger_unregister; >> + } >> + } >> + >> + ret = iio_device_register(indio_dev); >> + if (ret < 0) { >> + dev_err(&client->dev, "unable to register iio device\n"); >> + goto err_buffer_cleanup; >> + } >> + >> + ret = pm_runtime_set_active(&client->dev); >> + if (ret) >> + goto err_iio_unregister; >> + >> + pm_runtime_enable(&client->dev); >> + pm_runtime_set_autosuspend_delay(&client->dev, >> + BMG160_AUTO_SUSPEND_DELAY_MS); >> + pm_runtime_use_autosuspend(&client->dev); >> + >> + return 0; >> + >> +err_iio_unregister: >> + iio_device_unregister(indio_dev); >> +err_buffer_cleanup: >> + if (data->dready_trig) >> + iio_triggered_buffer_cleanup(indio_dev); >> +err_trigger_unregister: >> + if (data->dready_trig) >> + iio_trigger_unregister(data->dready_trig); >> + if (data->motion_trig) >> + iio_trigger_unregister(data->motion_trig); >> + >> + return ret; >> +} >> + >> +static int bmg160_remove(struct i2c_client *client) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + pm_runtime_disable(&client->dev); >> + pm_runtime_set_suspended(&client->dev); >> + pm_runtime_put_noidle(&client->dev); >> + >> + iio_device_unregister(indio_dev); >> + >> + if (data->dready_trig) { >> + iio_triggered_buffer_cleanup(indio_dev); >> + iio_trigger_unregister(data->dready_trig); >> + iio_trigger_unregister(data->motion_trig); >> + } >> + >> + mutex_lock(&data->mutex); >> + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +#ifdef CONFIG_PM_SLEEP >> +static int bmg160_suspend(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->mutex); >> + bmg160_set_mode(data, BMG160_MODE_SUSPEND); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> + >> +static int bmg160_resume(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->mutex); >> + if (data->dready_trigger_on || data->motion_trigger_on || >> + data->ev_enable_state) >> + bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + mutex_unlock(&data->mutex); >> + >> + return 0; >> +} >> +#endif >> + >> +#ifdef CONFIG_PM_RUNTIME >> +static int bmg160_runtime_suspend(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + >> + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); >> +} >> + >> +static int bmg160_runtime_resume(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct bmg160_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >> + if (ret < 0) >> + return ret; >> + >> + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); >> + >> + return 0; >> +} >> +#endif >> + >> +static const struct dev_pm_ops bmg160_pm_ops = { >> + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) >> + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, >> + bmg160_runtime_resume, NULL) >> +}; >> + >> +static const struct acpi_device_id bmg160_acpi_match[] = { >> + {"BMG0160", 0}, >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); >> + >> +static const struct i2c_device_id bmg160_id[] = { >> + {"bmg160", 0}, >> + {} >> +}; >> + >> +MODULE_DEVICE_TABLE(i2c, bmg160_id); >> + >> +static struct i2c_driver bmg160_driver = { >> + .driver = { >> + .name = BMG160_DRV_NAME, >> + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), >> + .pm = &bmg160_pm_ops, >> + }, >> + .probe = bmg160_probe, >> + .remove = bmg160_remove, >> + .id_table = bmg160_id, >> +}; >> +module_i2c_driver(bmg160_driver); >> + >> +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_DESCRIPTION("BMG160 Gyro driver"); >> > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-12 19:06 ` Srinivas Pandruvada @ 2014-08-19 19:51 ` Jonathan Cameron 2014-08-26 17:37 ` Jonathan Cameron 0 siblings, 1 reply; 8+ messages in thread From: Jonathan Cameron @ 2014-08-19 19:51 UTC (permalink / raw) To: Srinivas Pandruvada; +Cc: linux-iio On 12/08/14 20:06, Srinivas Pandruvada wrote: > On 08/07/2014 03:39 AM, Jonathan Cameron wrote: >> On 05/08/14 22:57, Srinivas Pandruvada wrote: >>> This change implements support for BMG160 Gyro sensor. Although chip >>> has several advanced features, this change implements minimum set >>> required for using gyro sensor. >>> >>> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> >> One more query. In the write_raw function you always enable then suspend the >> device. The enable is safe, but if you have a write to the attributes >> whilst operating in triggered / buffered mode will it not disable the >> device? It is perfectly permissible to ignore such a write (return -EBUSY) >> but I don't think you are doing so. >> > > I tested this. When trigger/events are active then first call to bmg160_set_power_state will increment runtime pm > refcount and also execute runtime resume to change the mode to NORMAL. > But when write_raw is called while the trigger/events are active the runtime PM refcount is incremented since it was > already more than 0, so the resume is not actually called. When at the end of write_raw bmg160_set_power_state to turn > off, the ref count is simply decremented, but since it is not zero, runtime suspend will not be called. > Sorry, managed to loose this driver so will pick it up after Greg has taken the pull request I'm just sending. Not exactly a lack of time at this point in the cycle ;) > Thanks, > Srinivas > >> Otherwise, looks good to me. Nice explanatory comments as well! >>> --- >>> drivers/iio/gyro/Kconfig | 11 + >>> drivers/iio/gyro/Makefile | 1 + >>> drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ >>> 3 files changed, 1223 insertions(+) >>> create mode 100644 drivers/iio/gyro/bmg160.c >>> >>> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig >>> index ac2d69e..d630ae9 100644 >>> --- a/drivers/iio/gyro/Kconfig >>> +++ b/drivers/iio/gyro/Kconfig >>> @@ -50,6 +50,17 @@ config ADXRS450 >>> This driver can also be built as a module. If so, the module >>> will be called adxrs450. >>> >>> +config BMG160 >>> + tristate "BOSCH BMG160 Gyro Sensor" >>> + depends on I2C >>> + select IIO_TRIGGERED_BUFFER if IIO_BUFFER >>> + help >>> + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor >>> + driver. >>> + >>> + This driver can also be built as a module. If so, the module >>> + will be called bmg160. >>> + >>> config HID_SENSOR_GYRO_3D >>> depends on HID_SENSOR_HUB >>> select IIO_BUFFER >>> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile >>> index 2f2752a..36a3877 100644 >>> --- a/drivers/iio/gyro/Makefile >>> +++ b/drivers/iio/gyro/Makefile >>> @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o >>> obj-$(CONFIG_ADIS16136) += adis16136.o >>> obj-$(CONFIG_ADIS16260) += adis16260.o >>> obj-$(CONFIG_ADXRS450) += adxrs450.o >>> +obj-$(CONFIG_BMG160) += bmg160.o >>> >>> obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o >>> >>> diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c >>> new file mode 100644 >>> index 0000000..80f92a6 >>> --- /dev/null >>> +++ b/drivers/iio/gyro/bmg160.c >>> @@ -0,0 +1,1211 @@ >>> +/* >>> + * BMG160 Gyro Sensor driver >>> + * Copyright (c) 2014, Intel Corporation. >>> + * >>> + * This program is free software; you can redistribute it and/or modify it >>> + * under the terms and conditions of the GNU General Public License, >>> + * version 2, as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope it will be useful, but WITHOUT >>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for >>> + * more details. >>> + */ >>> + >>> +#include <linux/module.h> >>> +#include <linux/i2c.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/delay.h> >>> +#include <linux/slab.h> >>> +#include <linux/acpi.h> >>> +#include <linux/gpio/consumer.h> >>> +#include <linux/pm.h> >>> +#include <linux/pm_runtime.h> >>> +#include <linux/iio/iio.h> >>> +#include <linux/iio/sysfs.h> >>> +#include <linux/iio/buffer.h> >>> +#include <linux/iio/trigger.h> >>> +#include <linux/iio/events.h> >>> +#include <linux/iio/trigger_consumer.h> >>> +#include <linux/iio/triggered_buffer.h> >>> + >>> +#define BMG160_DRV_NAME "bmg160" >>> +#define BMG160_IRQ_NAME "bmg160_event" >>> +#define BMG160_GPIO_NAME "gpio_int" >>> + >>> +#define BMG160_REG_CHIP_ID 0x00 >>> +#define BMG160_CHIP_ID_VAL 0x0F >>> + >>> +#define BMG160_REG_PMU_LPW 0x11 >>> +#define BMG160_MODE_NORMAL 0x00 >>> +#define BMG160_MODE_DEEP_SUSPEND 0x20 >>> +#define BMG160_MODE_SUSPEND 0x80 >>> + >>> +#define BMG160_REG_RANGE 0x0F >>> + >>> +#define BMG160_RANGE_2000DPS 0 >>> +#define BMG160_RANGE_1000DPS 1 >>> +#define BMG160_RANGE_500DPS 2 >>> +#define BMG160_RANGE_250DPS 3 >>> +#define BMG160_RANGE_125DPS 4 >>> + >>> +#define BMG160_REG_PMU_BW 0x10 >>> +#define BMG160_NO_FILTER 0 >>> +#define BMG160_DEF_BW 100 >>> + >>> +#define BMG160_REG_INT_MAP_0 0x17 >>> +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) >>> + >>> +#define BMG160_REG_INT_MAP_1 0x18 >>> +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) >>> + >>> +#define BMG160_REG_INT_RST_LATCH 0x21 >>> +#define BMG160_INT_MODE_LATCH_RESET 0x80 >>> +#define BMG160_INT_MODE_LATCH_INT 0x0F >>> +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 >>> + >>> +#define BMG160_REG_INT_EN_0 0x15 >>> +#define BMG160_DATA_ENABLE_INT BIT(7) >>> + >>> +#define BMG160_REG_XOUT_L 0x02 >>> +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) >>> + >>> +#define BMG160_REG_SLOPE_THRES 0x1B >>> +#define BMG160_SLOPE_THRES_MASK 0x0F >>> + >>> +#define BMG160_REG_MOTION_INTR 0x1C >>> +#define BMG160_INT_MOTION_X BIT(0) >>> +#define BMG160_INT_MOTION_Y BIT(1) >>> +#define BMG160_INT_MOTION_Z BIT(2) >>> +#define BMG160_ANY_DUR_MASK 0x30 >>> +#define BMG160_ANY_DUR_SHIFT 4 >>> + >>> +#define BMG160_REG_INT_STATUS_2 0x0B >>> +#define BMG160_ANY_MOTION_MASK 0x07 >>> + >>> +#define BMG160_REG_TEMP 0x08 >>> +#define BMG160_TEMP_CENTER_VAL 23 >>> + >>> +#define BMG160_MAX_STARTUP_TIME_MS 80 >>> + >>> +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 >>> + >>> +struct bmg160_data { >>> + struct i2c_client *client; >>> + struct iio_trigger *dready_trig; >>> + struct iio_trigger *motion_trig; >>> + struct mutex mutex; >>> + s16 buffer[8]; >>> + u8 bw_bits; >>> + u32 dps_range; >>> + int ev_enable_state; >>> + int slope_thres; >>> + bool dready_trigger_on; >>> + bool motion_trigger_on; >>> + int64_t timestamp; >>> +}; >>> + >>> +enum bmg160_axis { >>> + AXIS_X, >>> + AXIS_Y, >>> + AXIS_Z, >>> +}; >>> + >>> +static const struct { >>> + int val; >>> + int bw_bits; >>> +} bmg160_samp_freq_table[] = { {100, 0x07}, >>> + {200, 0x06}, >>> + {400, 0x03}, >>> + {1000, 0x02}, >>> + {2000, 0x01} }; >>> + >>> +static const struct { >>> + int scale; >>> + int dps_range; >>> +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, >>> + { 532, BMG160_RANGE_1000DPS}, >>> + { 266, BMG160_RANGE_500DPS}, >>> + { 133, BMG160_RANGE_250DPS}, >>> + { 66, BMG160_RANGE_125DPS} }; >>> + >>> +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) >>> +{ >>> + int ret; >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_PMU_LPW, mode); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_convert_freq_to_bit(int val) >>> +{ >>> + int i; >>> + >>> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >>> + if (bmg160_samp_freq_table[i].val == val) >>> + return bmg160_samp_freq_table[i].bw_bits; >>> + } >>> + >>> + return -EINVAL; >>> +} >>> + >>> +static int bmg160_set_bw(struct bmg160_data *data, int val) >>> +{ >>> + int ret; >>> + int bw_bits; >>> + >>> + bw_bits = bmg160_convert_freq_to_bit(val); >>> + if (bw_bits < 0) >>> + return bw_bits; >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, >>> + bw_bits); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); >>> + return ret; >>> + } >>> + >>> + data->bw_bits = bw_bits; >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_chip_init(struct bmg160_data *data) >>> +{ >>> + int ret; >>> + >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); >>> + return ret; >>> + } >>> + >>> + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); >>> + if (ret != BMG160_CHIP_ID_VAL) { >>> + dev_err(&data->client->dev, "invalid chip %x\n", ret); >>> + return -ENODEV; >>> + } >>> + >>> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >>> + if (ret < 0) >>> + return ret; >>> + >>> + /* Wait upto 500 ms to be ready after changing mode */ >>> + usleep_range(500, 1000); >>> + >>> + /* Set Bandwidth */ >>> + ret = bmg160_set_bw(data, BMG160_DEF_BW); >>> + if (ret < 0) >>> + return ret; >>> + >>> + /* Set Default Range */ >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_RANGE, >>> + BMG160_RANGE_500DPS); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_range\n"); >>> + return ret; >>> + } >>> + data->dps_range = BMG160_RANGE_500DPS; >>> + >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); >>> + return ret; >>> + } >>> + data->slope_thres = ret; >>> + >>> + /* Set default interrupt mode */ >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_motion_intr\n"); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_set_power_state(struct bmg160_data *data, bool on) >>> +{ >>> + int ret; >>> + >>> + if (on) >>> + ret = pm_runtime_get_sync(&data->client->dev); >>> + else { >>> + pm_runtime_mark_last_busy(&data->client->dev); >>> + ret = pm_runtime_put_autosuspend(&data->client->dev); >>> + } >>> + >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Failed: bmg160_set_power_state for %d\n", on); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, >>> + bool status) >>> +{ >>> + int ret; >>> + >>> + /* Enable/Disable INT_MAP0 mapping */ >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); >>> + return ret; >>> + } >>> + if (status) >>> + ret |= BMG160_INT_MAP_0_BIT_ANY; >>> + else >>> + ret &= ~BMG160_INT_MAP_0_BIT_ANY; >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_MAP_0, >>> + ret); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); >>> + return ret; >>> + } >>> + >>> + /* Enable/Disable slope interrupts */ >>> + if (status) { >>> + /* Update slope thres */ >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_SLOPE_THRES, >>> + data->slope_thres); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_slope_thres\n"); >>> + return ret; >>> + } >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_MOTION_INTR, >>> + BMG160_INT_MOTION_X | >>> + BMG160_INT_MOTION_Y | >>> + BMG160_INT_MOTION_Z); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_motion_intr\n"); >>> + return ret; >>> + } >>> + >>> + /* >>> + * New data interrupt is always non-latched, >>> + * which will have higher priority, so no need >>> + * to set latched mode, we will be flooded anyway with INTR >>> + */ >>> + if (!data->dready_trigger_on) { >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_rst_latch\n"); >>> + return ret; >>> + } >>> + } >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_EN_0, >>> + BMG160_DATA_ENABLE_INT); >>> + >>> + } else >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_EN_0, >>> + 0); >>> + >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, >>> + bool status) >>> +{ >>> + int ret; >>> + >>> + /* Enable/Disable INT_MAP1 mapping */ >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); >>> + return ret; >>> + } >>> + >>> + if (status) >>> + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; >>> + else >>> + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_MAP_1, >>> + ret); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); >>> + return ret; >>> + } >>> + >>> + if (status) { >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_NON_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_rst_latch\n"); >>> + return ret; >>> + } >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_EN_0, >>> + BMG160_DATA_ENABLE_INT); >>> + >>> + } else { >>> + /* Restore interrupt mode */ >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_rst_latch\n"); >>> + return ret; >>> + } >>> + >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_EN_0, >>> + 0); >>> + } >>> + >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_get_bw(struct bmg160_data *data, int *val) >>> +{ >>> + int i; >>> + >>> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >>> + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { >>> + *val = bmg160_samp_freq_table[i].val; >>> + return IIO_VAL_INT; >>> + } >>> + } >>> + >>> + return -EINVAL; >>> +} >>> + >>> +static int bmg160_set_scale(struct bmg160_data *data, int val) >>> +{ >>> + int ret, i; >>> + >>> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >>> + if (bmg160_scale_table[i].scale == val) { >>> + ret = i2c_smbus_write_byte_data( >>> + data->client, >>> + BMG160_REG_RANGE, >>> + bmg160_scale_table[i].dps_range); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, >>> + "Error writing reg_range\n"); >>> + return ret; >>> + } >>> + data->dps_range = bmg160_scale_table[i].dps_range; >>> + return 0; >>> + } >>> + } >>> + >>> + return -EINVAL; >>> +} >>> + >>> +static int bmg160_get_temp(struct bmg160_data *data, int *val) >>> +{ >>> + int ret; >>> + >>> + mutex_lock(&data->mutex); >>> + ret = bmg160_set_power_state(data, true); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_temp\n"); >>> + bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + *val = sign_extend32(ret, 7); >>> + ret = bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + if (ret < 0) >>> + return ret; >>> + >>> + return IIO_VAL_INT; >>> +} >>> + >>> +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) >>> +{ >>> + int ret; >>> + >>> + mutex_lock(&data->mutex); >>> + ret = bmg160_set_power_state(data, true); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading axis %d\n", axis); >>> + bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + *val = sign_extend32(ret, 15); >>> + ret = bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + if (ret < 0) >>> + return ret; >>> + >>> + return IIO_VAL_INT; >>> +} >>> + >>> +static int bmg160_read_raw(struct iio_dev *indio_dev, >>> + struct iio_chan_spec const *chan, >>> + int *val, int *val2, long mask) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + switch (mask) { >>> + case IIO_CHAN_INFO_RAW: >>> + switch (chan->type) { >>> + case IIO_TEMP: >>> + return bmg160_get_temp(data, val); >>> + case IIO_ANGL_VEL: >>> + if (iio_buffer_enabled(indio_dev)) >>> + return -EBUSY; >>> + else >>> + return bmg160_get_axis(data, chan->scan_index, >>> + val); >>> + default: >>> + return -EINVAL; >>> + } >>> + case IIO_CHAN_INFO_OFFSET: >>> + if (chan->type == IIO_TEMP) { >>> + *val = BMG160_TEMP_CENTER_VAL; >>> + return IIO_VAL_INT; >>> + } else >>> + return -EINVAL; >>> + case IIO_CHAN_INFO_SCALE: >>> + *val = 0; >>> + switch (chan->type) { >>> + case IIO_TEMP: >>> + *val2 = 500000; >>> + return IIO_VAL_INT_PLUS_MICRO; >>> + case IIO_ANGL_VEL: >>> + { >>> + int i; >>> + >>> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >>> + if (bmg160_scale_table[i].dps_range == >>> + data->dps_range) { >>> + *val2 = bmg160_scale_table[i].scale; >>> + return IIO_VAL_INT_PLUS_MICRO; >>> + } >>> + } >>> + return -EINVAL; >>> + } >>> + default: >>> + return -EINVAL; >>> + } >>> + case IIO_CHAN_INFO_SAMP_FREQ: >>> + *val2 = 0; >>> + mutex_lock(&data->mutex); >>> + ret = bmg160_get_bw(data, val); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + default: >>> + return -EINVAL; >>> + } >>> +} >>> + >>> +static int bmg160_write_raw(struct iio_dev *indio_dev, >>> + struct iio_chan_spec const *chan, >>> + int val, int val2, long mask) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + switch (mask) { >>> + case IIO_CHAN_INFO_SAMP_FREQ: >>> + mutex_lock(&data->mutex); >>> + /* >>> + * Section 4.2 of spec >>> + * In suspend mode, the only supported operations are reading >>> + * registers as well as writing to the (0x14) softreset >>> + * register. Since we will be in suspend mode by default, change >>> + * mode to power on for other writes. >>> + */ >>> + ret = bmg160_set_power_state(data, true); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + ret = bmg160_set_bw(data, val); >>> + if (ret < 0) { >>> + bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + ret = bmg160_set_power_state(data, false); >> Could this end up disabling the part when in triggered mode? >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + case IIO_CHAN_INFO_SCALE: >>> + if (val) >>> + return -EINVAL; >>> + >>> + mutex_lock(&data->mutex); >>> + /* Refer to comments above for the suspend mode ops */ >>> + ret = bmg160_set_power_state(data, true); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + ret = bmg160_set_scale(data, val2); >>> + if (ret < 0) { >>> + bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + ret = bmg160_set_power_state(data, false); >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + default: >>> + return -EINVAL; >>> + } >>> + >>> + return -EINVAL; >>> +} >>> + >>> +static int bmg160_read_event(struct iio_dev *indio_dev, >>> + const struct iio_chan_spec *chan, >>> + enum iio_event_type type, >>> + enum iio_event_direction dir, >>> + enum iio_event_info info, >>> + int *val, int *val2) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + *val2 = 0; >>> + switch (info) { >>> + case IIO_EV_INFO_VALUE: >>> + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; >>> + break; >>> + default: >>> + return -EINVAL; >>> + } >>> + >>> + return IIO_VAL_INT; >>> +} >>> + >>> +static int bmg160_write_event(struct iio_dev *indio_dev, >>> + const struct iio_chan_spec *chan, >>> + enum iio_event_type type, >>> + enum iio_event_direction dir, >>> + enum iio_event_info info, >>> + int val, int val2) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + switch (info) { >>> + case IIO_EV_INFO_VALUE: >>> + if (data->ev_enable_state) >>> + return -EBUSY; >>> + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; >>> + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); >>> + break; >>> + default: >>> + return -EINVAL; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_read_event_config(struct iio_dev *indio_dev, >>> + const struct iio_chan_spec *chan, >>> + enum iio_event_type type, >>> + enum iio_event_direction dir) >>> +{ >>> + >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + return data->ev_enable_state; >>> +} >>> + >>> +static int bmg160_write_event_config(struct iio_dev *indio_dev, >>> + const struct iio_chan_spec *chan, >>> + enum iio_event_type type, >>> + enum iio_event_direction dir, >>> + int state) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + if (state && data->ev_enable_state) >>> + return 0; >>> + >>> + mutex_lock(&data->mutex); >>> + >>> + if (!state && data->motion_trigger_on) { >>> + data->ev_enable_state = 0; >>> + mutex_unlock(&data->mutex); >>> + return 0; >>> + } >>> + /* >>> + * We will expect the enable and disable to do operation in >>> + * in reverse order. This will happen here anyway as our >>> + * resume operation uses sync mode runtime pm calls, the >>> + * suspend operation will be delayed by autosuspend delay >>> + * So the disable operation will still happen in reverse of >>> + * enable operation. When runtime pm is disabled the mode >>> + * is always on so sequence doesn't matter >>> + */ >> Thanks for the detailed comment! >>> + ret = bmg160_set_power_state(data, state); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + ret = bmg160_setup_any_motion_interrupt(data, state); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + >>> + data->ev_enable_state = state; >>> + mutex_unlock(&data->mutex); >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_validate_trigger(struct iio_dev *indio_dev, >>> + struct iio_trigger *trig) >>> +{ >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + if (data->dready_trig != trig && data->motion_trig != trig) >>> + return -EINVAL; >>> + >>> + return 0; >>> +} >>> + >>> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); >>> + >>> +static IIO_CONST_ATTR(in_anglvel_scale_available, >>> + "0.001065 0.000532 0.000266 0.000133 0.000066"); >>> + >>> +static struct attribute *bmg160_attributes[] = { >>> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, >>> + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, >>> + NULL, >>> +}; >>> + >>> +static const struct attribute_group bmg160_attrs_group = { >>> + .attrs = bmg160_attributes, >>> +}; >>> + >>> +static const struct iio_event_spec bmg160_event = { >>> + .type = IIO_EV_TYPE_ROC, >>> + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, >>> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | >>> + BIT(IIO_EV_INFO_ENABLE) >>> +}; >>> + >>> +#define BMG160_CHANNEL(_axis) { \ >>> + .type = IIO_ANGL_VEL, \ >>> + .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_index = AXIS_##_axis, \ >>> + .scan_type = { \ >>> + .sign = 's', \ >>> + .realbits = 16, \ >>> + .storagebits = 16, \ >>> + }, \ >>> + .event_spec = &bmg160_event, \ >>> + .num_event_specs = 1 \ >>> +} >>> + >>> +static const struct iio_chan_spec bmg160_channels[] = { >>> + { >>> + .type = IIO_TEMP, >>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >>> + BIT(IIO_CHAN_INFO_SCALE) | >>> + BIT(IIO_CHAN_INFO_OFFSET), >>> + .scan_index = -1, >>> + }, >>> + BMG160_CHANNEL(X), >>> + BMG160_CHANNEL(Y), >>> + BMG160_CHANNEL(Z), >>> + IIO_CHAN_SOFT_TIMESTAMP(3), >>> +}; >>> + >>> +static const struct iio_info bmg160_info = { >>> + .attrs = &bmg160_attrs_group, >>> + .read_raw = bmg160_read_raw, >>> + .write_raw = bmg160_write_raw, >>> + .read_event_value = bmg160_read_event, >>> + .write_event_value = bmg160_write_event, >>> + .write_event_config = bmg160_write_event_config, >>> + .read_event_config = bmg160_read_event_config, >>> + .validate_trigger = bmg160_validate_trigger, >>> + .driver_module = THIS_MODULE, >>> +}; >>> + >>> +static irqreturn_t bmg160_trigger_handler(int irq, void *p) >>> +{ >>> + struct iio_poll_func *pf = p; >>> + struct iio_dev *indio_dev = pf->indio_dev; >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int bit, ret, i = 0; >>> + >>> + mutex_lock(&data->mutex); >>> + for_each_set_bit(bit, indio_dev->buffer->scan_mask, >>> + indio_dev->masklength) { >>> + ret = i2c_smbus_read_word_data(data->client, >>> + BMG160_AXIS_TO_REG(bit)); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + goto err; >>> + } >>> + data->buffer[i++] = ret; >>> + } >>> + mutex_unlock(&data->mutex); >>> + >>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, >>> + data->timestamp); >>> +err: >>> + iio_trigger_notify_done(indio_dev->trig); >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static int bmg160_trig_try_reen(struct iio_trigger *trig) >>> +{ >>> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + /* new data interrupts don't need ack */ >>> + if (data->dready_trigger_on) >>> + return 0; >>> + >>> + /* Set latched mode interrupt and clear any latched interrupt */ >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, >>> + bool state) >>> +{ >>> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + mutex_lock(&data->mutex); >>> + >>> + if (!state && data->ev_enable_state && data->motion_trigger_on) { >>> + data->motion_trigger_on = false; >>> + mutex_unlock(&data->mutex); >>> + return 0; >>> + } >>> + >>> + /* >>> + * Refer to comment in bmg160_write_event_config for >>> + * enable/disable operation order >>> + */ >>> + ret = bmg160_set_power_state(data, state); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + if (data->motion_trig == trig) >>> + ret = bmg160_setup_any_motion_interrupt(data, state); >>> + else >>> + ret = bmg160_setup_new_data_interrupt(data, state); >>> + if (ret < 0) { >>> + mutex_unlock(&data->mutex); >>> + return ret; >>> + } >>> + if (data->motion_trig == trig) >>> + data->motion_trigger_on = state; >>> + else >>> + data->dready_trigger_on = state; >>> + >>> + mutex_unlock(&data->mutex); >>> + >>> + return 0; >>> +} >>> + >>> +static const struct iio_trigger_ops bmg160_trigger_ops = { >>> + .set_trigger_state = bmg160_data_rdy_trigger_set_state, >>> + .try_reenable = bmg160_trig_try_reen, >>> + .owner = THIS_MODULE, >>> +}; >>> + >>> +static irqreturn_t bmg160_event_handler(int irq, void *private) >>> +{ >>> + struct iio_dev *indio_dev = private; >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + int dir; >>> + >>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); >>> + if (ret < 0) { >>> + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); >>> + goto ack_intr_status; >>> + } >>> + >>> + if (ret & 0x08) >>> + dir = IIO_EV_DIR_RISING; >>> + else >>> + dir = IIO_EV_DIR_FALLING; >>> + >>> + if (ret & BMG160_ANY_MOTION_MASK) >>> + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, >>> + 0, >>> + IIO_MOD_X_OR_Y_OR_Z, >>> + IIO_EV_TYPE_ROC, >>> + dir), >>> + data->timestamp); >>> + >>> +ack_intr_status: >>> + if (!data->dready_trigger_on) { >>> + ret = i2c_smbus_write_byte_data(data->client, >>> + BMG160_REG_INT_RST_LATCH, >>> + BMG160_INT_MODE_LATCH_INT | >>> + BMG160_INT_MODE_LATCH_RESET); >>> + if (ret < 0) >>> + dev_err(&data->client->dev, >>> + "Error writing reg_rst_latch\n"); >>> + } >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) >>> +{ >>> + struct iio_dev *indio_dev = private; >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + data->timestamp = iio_get_time_ns(); >>> + >>> + if (data->dready_trigger_on) >>> + iio_trigger_poll(data->dready_trig); >>> + else if (data->motion_trigger_on) >>> + iio_trigger_poll(data->motion_trig); >>> + >>> + if (data->ev_enable_state) >>> + return IRQ_WAKE_THREAD; >>> + else >>> + return IRQ_HANDLED; >>> + >>> +} >>> + >>> +static int bmg160_acpi_gpio_probe(struct i2c_client *client, >>> + struct bmg160_data *data) >>> +{ >>> + const struct acpi_device_id *id; >>> + struct device *dev; >>> + struct gpio_desc *gpio; >>> + int ret; >>> + >>> + if (!client) >>> + return -EINVAL; >>> + >>> + dev = &client->dev; >>> + if (!ACPI_HANDLE(dev)) >>> + return -ENODEV; >>> + >>> + id = acpi_match_device(dev->driver->acpi_match_table, dev); >>> + if (!id) >>> + return -ENODEV; >>> + >>> + /* data ready gpio interrupt pin */ >>> + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); >>> + if (IS_ERR(gpio)) { >>> + dev_err(dev, "acpi gpio get index failed\n"); >>> + return PTR_ERR(gpio); >>> + } >>> + >>> + ret = gpiod_direction_input(gpio); >>> + if (ret) >>> + return ret; >>> + >>> + ret = gpiod_to_irq(gpio); >>> + >>> + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); >>> + >>> + return ret; >>> +} >>> + >>> +static int bmg160_probe(struct i2c_client *client, >>> + const struct i2c_device_id *id) >>> +{ >>> + struct bmg160_data *data; >>> + struct iio_dev *indio_dev; >>> + int ret; >>> + >>> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >>> + if (!indio_dev) >>> + return -ENOMEM; >>> + >>> + data = iio_priv(indio_dev); >>> + i2c_set_clientdata(client, indio_dev); >>> + data->client = client; >>> + >>> + ret = bmg160_chip_init(data); >>> + if (ret < 0) >>> + return ret; >>> + >>> + mutex_init(&data->mutex); >>> + >>> + indio_dev->dev.parent = &client->dev; >>> + indio_dev->channels = bmg160_channels; >>> + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); >>> + indio_dev->name = BMG160_DRV_NAME; >>> + indio_dev->modes = INDIO_DIRECT_MODE; >>> + indio_dev->info = &bmg160_info; >>> + >>> + if (client->irq <= 0) >>> + client->irq = bmg160_acpi_gpio_probe(client, data); >>> + >>> + if (client->irq > 0) { >>> + ret = devm_request_threaded_irq(&client->dev, >>> + client->irq, >>> + bmg160_data_rdy_trig_poll, >>> + bmg160_event_handler, >>> + IRQF_TRIGGER_RISING, >>> + BMG160_IRQ_NAME, >>> + indio_dev); >>> + if (ret) >>> + return ret; >>> + >>> + data->dready_trig = devm_iio_trigger_alloc(&client->dev, >>> + "%s-dev%d", >>> + indio_dev->name, >>> + indio_dev->id); >>> + if (!data->dready_trig) >>> + return -ENOMEM; >>> + >>> + data->motion_trig = devm_iio_trigger_alloc(&client->dev, >>> + "%s-any-motion-dev%d", >>> + indio_dev->name, >>> + indio_dev->id); >>> + if (!data->motion_trig) >>> + return -ENOMEM; >>> + >>> + data->dready_trig->dev.parent = &client->dev; >>> + data->dready_trig->ops = &bmg160_trigger_ops; >>> + iio_trigger_set_drvdata(data->dready_trig, indio_dev); >>> + ret = iio_trigger_register(data->dready_trig); >>> + if (ret) >>> + return ret; >>> + >>> + data->motion_trig->dev.parent = &client->dev; >>> + data->motion_trig->ops = &bmg160_trigger_ops; >>> + iio_trigger_set_drvdata(data->motion_trig, indio_dev); >>> + ret = iio_trigger_register(data->motion_trig); >>> + if (ret) { >>> + data->motion_trig = NULL; >>> + goto err_trigger_unregister; >>> + } >>> + >>> + ret = iio_triggered_buffer_setup(indio_dev, >>> + NULL, >>> + bmg160_trigger_handler, >>> + NULL); >>> + if (ret < 0) { >>> + dev_err(&client->dev, >>> + "iio triggered buffer setup failed\n"); >>> + goto err_trigger_unregister; >>> + } >>> + } >>> + >>> + ret = iio_device_register(indio_dev); >>> + if (ret < 0) { >>> + dev_err(&client->dev, "unable to register iio device\n"); >>> + goto err_buffer_cleanup; >>> + } >>> + >>> + ret = pm_runtime_set_active(&client->dev); >>> + if (ret) >>> + goto err_iio_unregister; >>> + >>> + pm_runtime_enable(&client->dev); >>> + pm_runtime_set_autosuspend_delay(&client->dev, >>> + BMG160_AUTO_SUSPEND_DELAY_MS); >>> + pm_runtime_use_autosuspend(&client->dev); >>> + >>> + return 0; >>> + >>> +err_iio_unregister: >>> + iio_device_unregister(indio_dev); >>> +err_buffer_cleanup: >>> + if (data->dready_trig) >>> + iio_triggered_buffer_cleanup(indio_dev); >>> +err_trigger_unregister: >>> + if (data->dready_trig) >>> + iio_trigger_unregister(data->dready_trig); >>> + if (data->motion_trig) >>> + iio_trigger_unregister(data->motion_trig); >>> + >>> + return ret; >>> +} >>> + >>> +static int bmg160_remove(struct i2c_client *client) >>> +{ >>> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + pm_runtime_disable(&client->dev); >>> + pm_runtime_set_suspended(&client->dev); >>> + pm_runtime_put_noidle(&client->dev); >>> + >>> + iio_device_unregister(indio_dev); >>> + >>> + if (data->dready_trig) { >>> + iio_triggered_buffer_cleanup(indio_dev); >>> + iio_trigger_unregister(data->dready_trig); >>> + iio_trigger_unregister(data->motion_trig); >>> + } >>> + >>> + mutex_lock(&data->mutex); >>> + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); >>> + mutex_unlock(&data->mutex); >>> + >>> + return 0; >>> +} >>> + >>> +#ifdef CONFIG_PM_SLEEP >>> +static int bmg160_suspend(struct device *dev) >>> +{ >>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + mutex_lock(&data->mutex); >>> + bmg160_set_mode(data, BMG160_MODE_SUSPEND); >>> + mutex_unlock(&data->mutex); >>> + >>> + return 0; >>> +} >>> + >>> +static int bmg160_resume(struct device *dev) >>> +{ >>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + mutex_lock(&data->mutex); >>> + if (data->dready_trigger_on || data->motion_trigger_on || >>> + data->ev_enable_state) >>> + bmg160_set_mode(data, BMG160_MODE_NORMAL); >>> + mutex_unlock(&data->mutex); >>> + >>> + return 0; >>> +} >>> +#endif >>> + >>> +#ifdef CONFIG_PM_RUNTIME >>> +static int bmg160_runtime_suspend(struct device *dev) >>> +{ >>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + >>> + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); >>> +} >>> + >>> +static int bmg160_runtime_resume(struct device *dev) >>> +{ >>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>> + struct bmg160_data *data = iio_priv(indio_dev); >>> + int ret; >>> + >>> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >>> + if (ret < 0) >>> + return ret; >>> + >>> + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); >>> + >>> + return 0; >>> +} >>> +#endif >>> + >>> +static const struct dev_pm_ops bmg160_pm_ops = { >>> + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) >>> + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, >>> + bmg160_runtime_resume, NULL) >>> +}; >>> + >>> +static const struct acpi_device_id bmg160_acpi_match[] = { >>> + {"BMG0160", 0}, >>> + { }, >>> +}; >>> +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); >>> + >>> +static const struct i2c_device_id bmg160_id[] = { >>> + {"bmg160", 0}, >>> + {} >>> +}; >>> + >>> +MODULE_DEVICE_TABLE(i2c, bmg160_id); >>> + >>> +static struct i2c_driver bmg160_driver = { >>> + .driver = { >>> + .name = BMG160_DRV_NAME, >>> + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), >>> + .pm = &bmg160_pm_ops, >>> + }, >>> + .probe = bmg160_probe, >>> + .remove = bmg160_remove, >>> + .id_table = bmg160_id, >>> +}; >>> +module_i2c_driver(bmg160_driver); >>> + >>> +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); >>> +MODULE_LICENSE("GPL v2"); >>> +MODULE_DESCRIPTION("BMG160 Gyro driver"); >>> >> > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] iio:gyro:bmg160 Gyro Sensor driver 2014-08-19 19:51 ` Jonathan Cameron @ 2014-08-26 17:37 ` Jonathan Cameron 0 siblings, 0 replies; 8+ messages in thread From: Jonathan Cameron @ 2014-08-26 17:37 UTC (permalink / raw) To: Srinivas Pandruvada; +Cc: linux-iio On 19/08/14 20:51, Jonathan Cameron wrote: > On 12/08/14 20:06, Srinivas Pandruvada wrote: >> On 08/07/2014 03:39 AM, Jonathan Cameron wrote: >>> On 05/08/14 22:57, Srinivas Pandruvada wrote: >>>> This change implements support for BMG160 Gyro sensor. Although chip >>>> has several advanced features, this change implements minimum set >>>> required for using gyro sensor. >>>> >>>> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> >>> One more query. In the write_raw function you always enable then suspend the >>> device. The enable is safe, but if you have a write to the attributes >>> whilst operating in triggered / buffered mode will it not disable the >>> device? It is perfectly permissible to ignore such a write (return -EBUSY) >>> but I don't think you are doing so. >>> >> >> I tested this. When trigger/events are active then first call to bmg160_set_power_state will increment runtime pm >> refcount and also execute runtime resume to change the mode to NORMAL. >> But when write_raw is called while the trigger/events are active the runtime PM refcount is incremented since it was >> already more than 0, so the resume is not actually called. When at the end of write_raw bmg160_set_power_state to turn >> off, the ref count is simply decremented, but since it is not zero, runtime suspend will not be called. >> > Sorry, managed to loose this driver so will pick it up after Greg > has taken the pull request I'm just sending. > > Not exactly a lack of time at this point in the cycle ;) Applied to the togreg branch of iio.git - initially pushed out as testing for the autobuilders to play. Thanks, Jonathan > >> Thanks, >> Srinivas >> >>> Otherwise, looks good to me. Nice explanatory comments as well! >>>> --- >>>> drivers/iio/gyro/Kconfig | 11 + >>>> drivers/iio/gyro/Makefile | 1 + >>>> drivers/iio/gyro/bmg160.c | 1211 +++++++++++++++++++++++++++++++++++++++++++++ >>>> 3 files changed, 1223 insertions(+) >>>> create mode 100644 drivers/iio/gyro/bmg160.c >>>> >>>> diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig >>>> index ac2d69e..d630ae9 100644 >>>> --- a/drivers/iio/gyro/Kconfig >>>> +++ b/drivers/iio/gyro/Kconfig >>>> @@ -50,6 +50,17 @@ config ADXRS450 >>>> This driver can also be built as a module. If so, the module >>>> will be called adxrs450. >>>> >>>> +config BMG160 >>>> + tristate "BOSCH BMG160 Gyro Sensor" >>>> + depends on I2C >>>> + select IIO_TRIGGERED_BUFFER if IIO_BUFFER >>>> + help >>>> + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor >>>> + driver. >>>> + >>>> + This driver can also be built as a module. If so, the module >>>> + will be called bmg160. >>>> + >>>> config HID_SENSOR_GYRO_3D >>>> depends on HID_SENSOR_HUB >>>> select IIO_BUFFER >>>> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile >>>> index 2f2752a..36a3877 100644 >>>> --- a/drivers/iio/gyro/Makefile >>>> +++ b/drivers/iio/gyro/Makefile >>>> @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16130) += adis16130.o >>>> obj-$(CONFIG_ADIS16136) += adis16136.o >>>> obj-$(CONFIG_ADIS16260) += adis16260.o >>>> obj-$(CONFIG_ADXRS450) += adxrs450.o >>>> +obj-$(CONFIG_BMG160) += bmg160.o >>>> >>>> obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o >>>> >>>> diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c >>>> new file mode 100644 >>>> index 0000000..80f92a6 >>>> --- /dev/null >>>> +++ b/drivers/iio/gyro/bmg160.c >>>> @@ -0,0 +1,1211 @@ >>>> +/* >>>> + * BMG160 Gyro Sensor driver >>>> + * Copyright (c) 2014, Intel Corporation. >>>> + * >>>> + * This program is free software; you can redistribute it and/or modify it >>>> + * under the terms and conditions of the GNU General Public License, >>>> + * version 2, as published by the Free Software Foundation. >>>> + * >>>> + * This program is distributed in the hope it will be useful, but WITHOUT >>>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >>>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for >>>> + * more details. >>>> + */ >>>> + >>>> +#include <linux/module.h> >>>> +#include <linux/i2c.h> >>>> +#include <linux/interrupt.h> >>>> +#include <linux/delay.h> >>>> +#include <linux/slab.h> >>>> +#include <linux/acpi.h> >>>> +#include <linux/gpio/consumer.h> >>>> +#include <linux/pm.h> >>>> +#include <linux/pm_runtime.h> >>>> +#include <linux/iio/iio.h> >>>> +#include <linux/iio/sysfs.h> >>>> +#include <linux/iio/buffer.h> >>>> +#include <linux/iio/trigger.h> >>>> +#include <linux/iio/events.h> >>>> +#include <linux/iio/trigger_consumer.h> >>>> +#include <linux/iio/triggered_buffer.h> >>>> + >>>> +#define BMG160_DRV_NAME "bmg160" >>>> +#define BMG160_IRQ_NAME "bmg160_event" >>>> +#define BMG160_GPIO_NAME "gpio_int" >>>> + >>>> +#define BMG160_REG_CHIP_ID 0x00 >>>> +#define BMG160_CHIP_ID_VAL 0x0F >>>> + >>>> +#define BMG160_REG_PMU_LPW 0x11 >>>> +#define BMG160_MODE_NORMAL 0x00 >>>> +#define BMG160_MODE_DEEP_SUSPEND 0x20 >>>> +#define BMG160_MODE_SUSPEND 0x80 >>>> + >>>> +#define BMG160_REG_RANGE 0x0F >>>> + >>>> +#define BMG160_RANGE_2000DPS 0 >>>> +#define BMG160_RANGE_1000DPS 1 >>>> +#define BMG160_RANGE_500DPS 2 >>>> +#define BMG160_RANGE_250DPS 3 >>>> +#define BMG160_RANGE_125DPS 4 >>>> + >>>> +#define BMG160_REG_PMU_BW 0x10 >>>> +#define BMG160_NO_FILTER 0 >>>> +#define BMG160_DEF_BW 100 >>>> + >>>> +#define BMG160_REG_INT_MAP_0 0x17 >>>> +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) >>>> + >>>> +#define BMG160_REG_INT_MAP_1 0x18 >>>> +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) >>>> + >>>> +#define BMG160_REG_INT_RST_LATCH 0x21 >>>> +#define BMG160_INT_MODE_LATCH_RESET 0x80 >>>> +#define BMG160_INT_MODE_LATCH_INT 0x0F >>>> +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 >>>> + >>>> +#define BMG160_REG_INT_EN_0 0x15 >>>> +#define BMG160_DATA_ENABLE_INT BIT(7) >>>> + >>>> +#define BMG160_REG_XOUT_L 0x02 >>>> +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) >>>> + >>>> +#define BMG160_REG_SLOPE_THRES 0x1B >>>> +#define BMG160_SLOPE_THRES_MASK 0x0F >>>> + >>>> +#define BMG160_REG_MOTION_INTR 0x1C >>>> +#define BMG160_INT_MOTION_X BIT(0) >>>> +#define BMG160_INT_MOTION_Y BIT(1) >>>> +#define BMG160_INT_MOTION_Z BIT(2) >>>> +#define BMG160_ANY_DUR_MASK 0x30 >>>> +#define BMG160_ANY_DUR_SHIFT 4 >>>> + >>>> +#define BMG160_REG_INT_STATUS_2 0x0B >>>> +#define BMG160_ANY_MOTION_MASK 0x07 >>>> + >>>> +#define BMG160_REG_TEMP 0x08 >>>> +#define BMG160_TEMP_CENTER_VAL 23 >>>> + >>>> +#define BMG160_MAX_STARTUP_TIME_MS 80 >>>> + >>>> +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 >>>> + >>>> +struct bmg160_data { >>>> + struct i2c_client *client; >>>> + struct iio_trigger *dready_trig; >>>> + struct iio_trigger *motion_trig; >>>> + struct mutex mutex; >>>> + s16 buffer[8]; >>>> + u8 bw_bits; >>>> + u32 dps_range; >>>> + int ev_enable_state; >>>> + int slope_thres; >>>> + bool dready_trigger_on; >>>> + bool motion_trigger_on; >>>> + int64_t timestamp; >>>> +}; >>>> + >>>> +enum bmg160_axis { >>>> + AXIS_X, >>>> + AXIS_Y, >>>> + AXIS_Z, >>>> +}; >>>> + >>>> +static const struct { >>>> + int val; >>>> + int bw_bits; >>>> +} bmg160_samp_freq_table[] = { {100, 0x07}, >>>> + {200, 0x06}, >>>> + {400, 0x03}, >>>> + {1000, 0x02}, >>>> + {2000, 0x01} }; >>>> + >>>> +static const struct { >>>> + int scale; >>>> + int dps_range; >>>> +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, >>>> + { 532, BMG160_RANGE_1000DPS}, >>>> + { 266, BMG160_RANGE_500DPS}, >>>> + { 133, BMG160_RANGE_250DPS}, >>>> + { 66, BMG160_RANGE_125DPS} }; >>>> + >>>> +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) >>>> +{ >>>> + int ret; >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_PMU_LPW, mode); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_convert_freq_to_bit(int val) >>>> +{ >>>> + int i; >>>> + >>>> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >>>> + if (bmg160_samp_freq_table[i].val == val) >>>> + return bmg160_samp_freq_table[i].bw_bits; >>>> + } >>>> + >>>> + return -EINVAL; >>>> +} >>>> + >>>> +static int bmg160_set_bw(struct bmg160_data *data, int val) >>>> +{ >>>> + int ret; >>>> + int bw_bits; >>>> + >>>> + bw_bits = bmg160_convert_freq_to_bit(val); >>>> + if (bw_bits < 0) >>>> + return bw_bits; >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, >>>> + bw_bits); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); >>>> + return ret; >>>> + } >>>> + >>>> + data->bw_bits = bw_bits; >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_chip_init(struct bmg160_data *data) >>>> +{ >>>> + int ret; >>>> + >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_chip_id\n"); >>>> + return ret; >>>> + } >>>> + >>>> + dev_dbg(&data->client->dev, "Chip Id %x\n", ret); >>>> + if (ret != BMG160_CHIP_ID_VAL) { >>>> + dev_err(&data->client->dev, "invalid chip %x\n", ret); >>>> + return -ENODEV; >>>> + } >>>> + >>>> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + /* Wait upto 500 ms to be ready after changing mode */ >>>> + usleep_range(500, 1000); >>>> + >>>> + /* Set Bandwidth */ >>>> + ret = bmg160_set_bw(data, BMG160_DEF_BW); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + /* Set Default Range */ >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_RANGE, >>>> + BMG160_RANGE_500DPS); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_range\n"); >>>> + return ret; >>>> + } >>>> + data->dps_range = BMG160_RANGE_500DPS; >>>> + >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); >>>> + return ret; >>>> + } >>>> + data->slope_thres = ret; >>>> + >>>> + /* Set default interrupt mode */ >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_motion_intr\n"); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_set_power_state(struct bmg160_data *data, bool on) >>>> +{ >>>> + int ret; >>>> + >>>> + if (on) >>>> + ret = pm_runtime_get_sync(&data->client->dev); >>>> + else { >>>> + pm_runtime_mark_last_busy(&data->client->dev); >>>> + ret = pm_runtime_put_autosuspend(&data->client->dev); >>>> + } >>>> + >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Failed: bmg160_set_power_state for %d\n", on); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, >>>> + bool status) >>>> +{ >>>> + int ret; >>>> + >>>> + /* Enable/Disable INT_MAP0 mapping */ >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_int_map0\n"); >>>> + return ret; >>>> + } >>>> + if (status) >>>> + ret |= BMG160_INT_MAP_0_BIT_ANY; >>>> + else >>>> + ret &= ~BMG160_INT_MAP_0_BIT_ANY; >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_MAP_0, >>>> + ret); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_int_map0\n"); >>>> + return ret; >>>> + } >>>> + >>>> + /* Enable/Disable slope interrupts */ >>>> + if (status) { >>>> + /* Update slope thres */ >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_SLOPE_THRES, >>>> + data->slope_thres); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_slope_thres\n"); >>>> + return ret; >>>> + } >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_MOTION_INTR, >>>> + BMG160_INT_MOTION_X | >>>> + BMG160_INT_MOTION_Y | >>>> + BMG160_INT_MOTION_Z); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_motion_intr\n"); >>>> + return ret; >>>> + } >>>> + >>>> + /* >>>> + * New data interrupt is always non-latched, >>>> + * which will have higher priority, so no need >>>> + * to set latched mode, we will be flooded anyway with INTR >>>> + */ >>>> + if (!data->dready_trigger_on) { >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_rst_latch\n"); >>>> + return ret; >>>> + } >>>> + } >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_EN_0, >>>> + BMG160_DATA_ENABLE_INT); >>>> + >>>> + } else >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_EN_0, >>>> + 0); >>>> + >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, >>>> + bool status) >>>> +{ >>>> + int ret; >>>> + >>>> + /* Enable/Disable INT_MAP1 mapping */ >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_int_map1\n"); >>>> + return ret; >>>> + } >>>> + >>>> + if (status) >>>> + ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; >>>> + else >>>> + ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_MAP_1, >>>> + ret); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_int_map1\n"); >>>> + return ret; >>>> + } >>>> + >>>> + if (status) { >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_NON_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_rst_latch\n"); >>>> + return ret; >>>> + } >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_EN_0, >>>> + BMG160_DATA_ENABLE_INT); >>>> + >>>> + } else { >>>> + /* Restore interrupt mode */ >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_rst_latch\n"); >>>> + return ret; >>>> + } >>>> + >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_EN_0, >>>> + 0); >>>> + } >>>> + >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_int_en0\n"); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_get_bw(struct bmg160_data *data, int *val) >>>> +{ >>>> + int i; >>>> + >>>> + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { >>>> + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { >>>> + *val = bmg160_samp_freq_table[i].val; >>>> + return IIO_VAL_INT; >>>> + } >>>> + } >>>> + >>>> + return -EINVAL; >>>> +} >>>> + >>>> +static int bmg160_set_scale(struct bmg160_data *data, int val) >>>> +{ >>>> + int ret, i; >>>> + >>>> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >>>> + if (bmg160_scale_table[i].scale == val) { >>>> + ret = i2c_smbus_write_byte_data( >>>> + data->client, >>>> + BMG160_REG_RANGE, >>>> + bmg160_scale_table[i].dps_range); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_range\n"); >>>> + return ret; >>>> + } >>>> + data->dps_range = bmg160_scale_table[i].dps_range; >>>> + return 0; >>>> + } >>>> + } >>>> + >>>> + return -EINVAL; >>>> +} >>>> + >>>> +static int bmg160_get_temp(struct bmg160_data *data, int *val) >>>> +{ >>>> + int ret; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + ret = bmg160_set_power_state(data, true); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_temp\n"); >>>> + bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + *val = sign_extend32(ret, 7); >>>> + ret = bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + return IIO_VAL_INT; >>>> +} >>>> + >>>> +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) >>>> +{ >>>> + int ret; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + ret = bmg160_set_power_state(data, true); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading axis %d\n", axis); >>>> + bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + *val = sign_extend32(ret, 15); >>>> + ret = bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + return IIO_VAL_INT; >>>> +} >>>> + >>>> +static int bmg160_read_raw(struct iio_dev *indio_dev, >>>> + struct iio_chan_spec const *chan, >>>> + int *val, int *val2, long mask) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + switch (mask) { >>>> + case IIO_CHAN_INFO_RAW: >>>> + switch (chan->type) { >>>> + case IIO_TEMP: >>>> + return bmg160_get_temp(data, val); >>>> + case IIO_ANGL_VEL: >>>> + if (iio_buffer_enabled(indio_dev)) >>>> + return -EBUSY; >>>> + else >>>> + return bmg160_get_axis(data, chan->scan_index, >>>> + val); >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + case IIO_CHAN_INFO_OFFSET: >>>> + if (chan->type == IIO_TEMP) { >>>> + *val = BMG160_TEMP_CENTER_VAL; >>>> + return IIO_VAL_INT; >>>> + } else >>>> + return -EINVAL; >>>> + case IIO_CHAN_INFO_SCALE: >>>> + *val = 0; >>>> + switch (chan->type) { >>>> + case IIO_TEMP: >>>> + *val2 = 500000; >>>> + return IIO_VAL_INT_PLUS_MICRO; >>>> + case IIO_ANGL_VEL: >>>> + { >>>> + int i; >>>> + >>>> + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { >>>> + if (bmg160_scale_table[i].dps_range == >>>> + data->dps_range) { >>>> + *val2 = bmg160_scale_table[i].scale; >>>> + return IIO_VAL_INT_PLUS_MICRO; >>>> + } >>>> + } >>>> + return -EINVAL; >>>> + } >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + case IIO_CHAN_INFO_SAMP_FREQ: >>>> + *val2 = 0; >>>> + mutex_lock(&data->mutex); >>>> + ret = bmg160_get_bw(data, val); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> +} >>>> + >>>> +static int bmg160_write_raw(struct iio_dev *indio_dev, >>>> + struct iio_chan_spec const *chan, >>>> + int val, int val2, long mask) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + switch (mask) { >>>> + case IIO_CHAN_INFO_SAMP_FREQ: >>>> + mutex_lock(&data->mutex); >>>> + /* >>>> + * Section 4.2 of spec >>>> + * In suspend mode, the only supported operations are reading >>>> + * registers as well as writing to the (0x14) softreset >>>> + * register. Since we will be in suspend mode by default, change >>>> + * mode to power on for other writes. >>>> + */ >>>> + ret = bmg160_set_power_state(data, true); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + ret = bmg160_set_bw(data, val); >>>> + if (ret < 0) { >>>> + bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + ret = bmg160_set_power_state(data, false); >>> Could this end up disabling the part when in triggered mode? >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + case IIO_CHAN_INFO_SCALE: >>>> + if (val) >>>> + return -EINVAL; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + /* Refer to comments above for the suspend mode ops */ >>>> + ret = bmg160_set_power_state(data, true); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + ret = bmg160_set_scale(data, val2); >>>> + if (ret < 0) { >>>> + bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + ret = bmg160_set_power_state(data, false); >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + >>>> + return -EINVAL; >>>> +} >>>> + >>>> +static int bmg160_read_event(struct iio_dev *indio_dev, >>>> + const struct iio_chan_spec *chan, >>>> + enum iio_event_type type, >>>> + enum iio_event_direction dir, >>>> + enum iio_event_info info, >>>> + int *val, int *val2) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + *val2 = 0; >>>> + switch (info) { >>>> + case IIO_EV_INFO_VALUE: >>>> + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; >>>> + break; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + >>>> + return IIO_VAL_INT; >>>> +} >>>> + >>>> +static int bmg160_write_event(struct iio_dev *indio_dev, >>>> + const struct iio_chan_spec *chan, >>>> + enum iio_event_type type, >>>> + enum iio_event_direction dir, >>>> + enum iio_event_info info, >>>> + int val, int val2) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + switch (info) { >>>> + case IIO_EV_INFO_VALUE: >>>> + if (data->ev_enable_state) >>>> + return -EBUSY; >>>> + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; >>>> + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); >>>> + break; >>>> + default: >>>> + return -EINVAL; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_read_event_config(struct iio_dev *indio_dev, >>>> + const struct iio_chan_spec *chan, >>>> + enum iio_event_type type, >>>> + enum iio_event_direction dir) >>>> +{ >>>> + >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + return data->ev_enable_state; >>>> +} >>>> + >>>> +static int bmg160_write_event_config(struct iio_dev *indio_dev, >>>> + const struct iio_chan_spec *chan, >>>> + enum iio_event_type type, >>>> + enum iio_event_direction dir, >>>> + int state) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + if (state && data->ev_enable_state) >>>> + return 0; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + >>>> + if (!state && data->motion_trigger_on) { >>>> + data->ev_enable_state = 0; >>>> + mutex_unlock(&data->mutex); >>>> + return 0; >>>> + } >>>> + /* >>>> + * We will expect the enable and disable to do operation in >>>> + * in reverse order. This will happen here anyway as our >>>> + * resume operation uses sync mode runtime pm calls, the >>>> + * suspend operation will be delayed by autosuspend delay >>>> + * So the disable operation will still happen in reverse of >>>> + * enable operation. When runtime pm is disabled the mode >>>> + * is always on so sequence doesn't matter >>>> + */ >>> Thanks for the detailed comment! >>>> + ret = bmg160_set_power_state(data, state); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + ret = bmg160_setup_any_motion_interrupt(data, state); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + >>>> + data->ev_enable_state = state; >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_validate_trigger(struct iio_dev *indio_dev, >>>> + struct iio_trigger *trig) >>>> +{ >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + if (data->dready_trig != trig && data->motion_trig != trig) >>>> + return -EINVAL; >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); >>>> + >>>> +static IIO_CONST_ATTR(in_anglvel_scale_available, >>>> + "0.001065 0.000532 0.000266 0.000133 0.000066"); >>>> + >>>> +static struct attribute *bmg160_attributes[] = { >>>> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, >>>> + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, >>>> + NULL, >>>> +}; >>>> + >>>> +static const struct attribute_group bmg160_attrs_group = { >>>> + .attrs = bmg160_attributes, >>>> +}; >>>> + >>>> +static const struct iio_event_spec bmg160_event = { >>>> + .type = IIO_EV_TYPE_ROC, >>>> + .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, >>>> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | >>>> + BIT(IIO_EV_INFO_ENABLE) >>>> +}; >>>> + >>>> +#define BMG160_CHANNEL(_axis) { \ >>>> + .type = IIO_ANGL_VEL, \ >>>> + .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_index = AXIS_##_axis, \ >>>> + .scan_type = { \ >>>> + .sign = 's', \ >>>> + .realbits = 16, \ >>>> + .storagebits = 16, \ >>>> + }, \ >>>> + .event_spec = &bmg160_event, \ >>>> + .num_event_specs = 1 \ >>>> +} >>>> + >>>> +static const struct iio_chan_spec bmg160_channels[] = { >>>> + { >>>> + .type = IIO_TEMP, >>>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >>>> + BIT(IIO_CHAN_INFO_SCALE) | >>>> + BIT(IIO_CHAN_INFO_OFFSET), >>>> + .scan_index = -1, >>>> + }, >>>> + BMG160_CHANNEL(X), >>>> + BMG160_CHANNEL(Y), >>>> + BMG160_CHANNEL(Z), >>>> + IIO_CHAN_SOFT_TIMESTAMP(3), >>>> +}; >>>> + >>>> +static const struct iio_info bmg160_info = { >>>> + .attrs = &bmg160_attrs_group, >>>> + .read_raw = bmg160_read_raw, >>>> + .write_raw = bmg160_write_raw, >>>> + .read_event_value = bmg160_read_event, >>>> + .write_event_value = bmg160_write_event, >>>> + .write_event_config = bmg160_write_event_config, >>>> + .read_event_config = bmg160_read_event_config, >>>> + .validate_trigger = bmg160_validate_trigger, >>>> + .driver_module = THIS_MODULE, >>>> +}; >>>> + >>>> +static irqreturn_t bmg160_trigger_handler(int irq, void *p) >>>> +{ >>>> + struct iio_poll_func *pf = p; >>>> + struct iio_dev *indio_dev = pf->indio_dev; >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int bit, ret, i = 0; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + for_each_set_bit(bit, indio_dev->buffer->scan_mask, >>>> + indio_dev->masklength) { >>>> + ret = i2c_smbus_read_word_data(data->client, >>>> + BMG160_AXIS_TO_REG(bit)); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + goto err; >>>> + } >>>> + data->buffer[i++] = ret; >>>> + } >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, >>>> + data->timestamp); >>>> +err: >>>> + iio_trigger_notify_done(indio_dev->trig); >>>> + >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +static int bmg160_trig_try_reen(struct iio_trigger *trig) >>>> +{ >>>> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + /* new data interrupts don't need ack */ >>>> + if (data->dready_trigger_on) >>>> + return 0; >>>> + >>>> + /* Set latched mode interrupt and clear any latched interrupt */ >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); >>>> + return ret; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, >>>> + bool state) >>>> +{ >>>> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + mutex_lock(&data->mutex); >>>> + >>>> + if (!state && data->ev_enable_state && data->motion_trigger_on) { >>>> + data->motion_trigger_on = false; >>>> + mutex_unlock(&data->mutex); >>>> + return 0; >>>> + } >>>> + >>>> + /* >>>> + * Refer to comment in bmg160_write_event_config for >>>> + * enable/disable operation order >>>> + */ >>>> + ret = bmg160_set_power_state(data, state); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + if (data->motion_trig == trig) >>>> + ret = bmg160_setup_any_motion_interrupt(data, state); >>>> + else >>>> + ret = bmg160_setup_new_data_interrupt(data, state); >>>> + if (ret < 0) { >>>> + mutex_unlock(&data->mutex); >>>> + return ret; >>>> + } >>>> + if (data->motion_trig == trig) >>>> + data->motion_trigger_on = state; >>>> + else >>>> + data->dready_trigger_on = state; >>>> + >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static const struct iio_trigger_ops bmg160_trigger_ops = { >>>> + .set_trigger_state = bmg160_data_rdy_trigger_set_state, >>>> + .try_reenable = bmg160_trig_try_reen, >>>> + .owner = THIS_MODULE, >>>> +}; >>>> + >>>> +static irqreturn_t bmg160_event_handler(int irq, void *private) >>>> +{ >>>> + struct iio_dev *indio_dev = private; >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + int dir; >>>> + >>>> + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); >>>> + if (ret < 0) { >>>> + dev_err(&data->client->dev, "Error reading reg_int_status2\n"); >>>> + goto ack_intr_status; >>>> + } >>>> + >>>> + if (ret & 0x08) >>>> + dir = IIO_EV_DIR_RISING; >>>> + else >>>> + dir = IIO_EV_DIR_FALLING; >>>> + >>>> + if (ret & BMG160_ANY_MOTION_MASK) >>>> + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, >>>> + 0, >>>> + IIO_MOD_X_OR_Y_OR_Z, >>>> + IIO_EV_TYPE_ROC, >>>> + dir), >>>> + data->timestamp); >>>> + >>>> +ack_intr_status: >>>> + if (!data->dready_trigger_on) { >>>> + ret = i2c_smbus_write_byte_data(data->client, >>>> + BMG160_REG_INT_RST_LATCH, >>>> + BMG160_INT_MODE_LATCH_INT | >>>> + BMG160_INT_MODE_LATCH_RESET); >>>> + if (ret < 0) >>>> + dev_err(&data->client->dev, >>>> + "Error writing reg_rst_latch\n"); >>>> + } >>>> + >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) >>>> +{ >>>> + struct iio_dev *indio_dev = private; >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + data->timestamp = iio_get_time_ns(); >>>> + >>>> + if (data->dready_trigger_on) >>>> + iio_trigger_poll(data->dready_trig); >>>> + else if (data->motion_trigger_on) >>>> + iio_trigger_poll(data->motion_trig); >>>> + >>>> + if (data->ev_enable_state) >>>> + return IRQ_WAKE_THREAD; >>>> + else >>>> + return IRQ_HANDLED; >>>> + >>>> +} >>>> + >>>> +static int bmg160_acpi_gpio_probe(struct i2c_client *client, >>>> + struct bmg160_data *data) >>>> +{ >>>> + const struct acpi_device_id *id; >>>> + struct device *dev; >>>> + struct gpio_desc *gpio; >>>> + int ret; >>>> + >>>> + if (!client) >>>> + return -EINVAL; >>>> + >>>> + dev = &client->dev; >>>> + if (!ACPI_HANDLE(dev)) >>>> + return -ENODEV; >>>> + >>>> + id = acpi_match_device(dev->driver->acpi_match_table, dev); >>>> + if (!id) >>>> + return -ENODEV; >>>> + >>>> + /* data ready gpio interrupt pin */ >>>> + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); >>>> + if (IS_ERR(gpio)) { >>>> + dev_err(dev, "acpi gpio get index failed\n"); >>>> + return PTR_ERR(gpio); >>>> + } >>>> + >>>> + ret = gpiod_direction_input(gpio); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + ret = gpiod_to_irq(gpio); >>>> + >>>> + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); >>>> + >>>> + return ret; >>>> +} >>>> + >>>> +static int bmg160_probe(struct i2c_client *client, >>>> + const struct i2c_device_id *id) >>>> +{ >>>> + struct bmg160_data *data; >>>> + struct iio_dev *indio_dev; >>>> + int ret; >>>> + >>>> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >>>> + if (!indio_dev) >>>> + return -ENOMEM; >>>> + >>>> + data = iio_priv(indio_dev); >>>> + i2c_set_clientdata(client, indio_dev); >>>> + data->client = client; >>>> + >>>> + ret = bmg160_chip_init(data); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + mutex_init(&data->mutex); >>>> + >>>> + indio_dev->dev.parent = &client->dev; >>>> + indio_dev->channels = bmg160_channels; >>>> + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); >>>> + indio_dev->name = BMG160_DRV_NAME; >>>> + indio_dev->modes = INDIO_DIRECT_MODE; >>>> + indio_dev->info = &bmg160_info; >>>> + >>>> + if (client->irq <= 0) >>>> + client->irq = bmg160_acpi_gpio_probe(client, data); >>>> + >>>> + if (client->irq > 0) { >>>> + ret = devm_request_threaded_irq(&client->dev, >>>> + client->irq, >>>> + bmg160_data_rdy_trig_poll, >>>> + bmg160_event_handler, >>>> + IRQF_TRIGGER_RISING, >>>> + BMG160_IRQ_NAME, >>>> + indio_dev); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + data->dready_trig = devm_iio_trigger_alloc(&client->dev, >>>> + "%s-dev%d", >>>> + indio_dev->name, >>>> + indio_dev->id); >>>> + if (!data->dready_trig) >>>> + return -ENOMEM; >>>> + >>>> + data->motion_trig = devm_iio_trigger_alloc(&client->dev, >>>> + "%s-any-motion-dev%d", >>>> + indio_dev->name, >>>> + indio_dev->id); >>>> + if (!data->motion_trig) >>>> + return -ENOMEM; >>>> + >>>> + data->dready_trig->dev.parent = &client->dev; >>>> + data->dready_trig->ops = &bmg160_trigger_ops; >>>> + iio_trigger_set_drvdata(data->dready_trig, indio_dev); >>>> + ret = iio_trigger_register(data->dready_trig); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + data->motion_trig->dev.parent = &client->dev; >>>> + data->motion_trig->ops = &bmg160_trigger_ops; >>>> + iio_trigger_set_drvdata(data->motion_trig, indio_dev); >>>> + ret = iio_trigger_register(data->motion_trig); >>>> + if (ret) { >>>> + data->motion_trig = NULL; >>>> + goto err_trigger_unregister; >>>> + } >>>> + >>>> + ret = iio_triggered_buffer_setup(indio_dev, >>>> + NULL, >>>> + bmg160_trigger_handler, >>>> + NULL); >>>> + if (ret < 0) { >>>> + dev_err(&client->dev, >>>> + "iio triggered buffer setup failed\n"); >>>> + goto err_trigger_unregister; >>>> + } >>>> + } >>>> + >>>> + ret = iio_device_register(indio_dev); >>>> + if (ret < 0) { >>>> + dev_err(&client->dev, "unable to register iio device\n"); >>>> + goto err_buffer_cleanup; >>>> + } >>>> + >>>> + ret = pm_runtime_set_active(&client->dev); >>>> + if (ret) >>>> + goto err_iio_unregister; >>>> + >>>> + pm_runtime_enable(&client->dev); >>>> + pm_runtime_set_autosuspend_delay(&client->dev, >>>> + BMG160_AUTO_SUSPEND_DELAY_MS); >>>> + pm_runtime_use_autosuspend(&client->dev); >>>> + >>>> + return 0; >>>> + >>>> +err_iio_unregister: >>>> + iio_device_unregister(indio_dev); >>>> +err_buffer_cleanup: >>>> + if (data->dready_trig) >>>> + iio_triggered_buffer_cleanup(indio_dev); >>>> +err_trigger_unregister: >>>> + if (data->dready_trig) >>>> + iio_trigger_unregister(data->dready_trig); >>>> + if (data->motion_trig) >>>> + iio_trigger_unregister(data->motion_trig); >>>> + >>>> + return ret; >>>> +} >>>> + >>>> +static int bmg160_remove(struct i2c_client *client) >>>> +{ >>>> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + pm_runtime_disable(&client->dev); >>>> + pm_runtime_set_suspended(&client->dev); >>>> + pm_runtime_put_noidle(&client->dev); >>>> + >>>> + iio_device_unregister(indio_dev); >>>> + >>>> + if (data->dready_trig) { >>>> + iio_triggered_buffer_cleanup(indio_dev); >>>> + iio_trigger_unregister(data->dready_trig); >>>> + iio_trigger_unregister(data->motion_trig); >>>> + } >>>> + >>>> + mutex_lock(&data->mutex); >>>> + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +#ifdef CONFIG_PM_SLEEP >>>> +static int bmg160_suspend(struct device *dev) >>>> +{ >>>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + mutex_lock(&data->mutex); >>>> + bmg160_set_mode(data, BMG160_MODE_SUSPEND); >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int bmg160_resume(struct device *dev) >>>> +{ >>>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + mutex_lock(&data->mutex); >>>> + if (data->dready_trigger_on || data->motion_trigger_on || >>>> + data->ev_enable_state) >>>> + bmg160_set_mode(data, BMG160_MODE_NORMAL); >>>> + mutex_unlock(&data->mutex); >>>> + >>>> + return 0; >>>> +} >>>> +#endif >>>> + >>>> +#ifdef CONFIG_PM_RUNTIME >>>> +static int bmg160_runtime_suspend(struct device *dev) >>>> +{ >>>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + >>>> + return bmg160_set_mode(data, BMG160_MODE_SUSPEND); >>>> +} >>>> + >>>> +static int bmg160_runtime_resume(struct device *dev) >>>> +{ >>>> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >>>> + struct bmg160_data *data = iio_priv(indio_dev); >>>> + int ret; >>>> + >>>> + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); >>>> + if (ret < 0) >>>> + return ret; >>>> + >>>> + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); >>>> + >>>> + return 0; >>>> +} >>>> +#endif >>>> + >>>> +static const struct dev_pm_ops bmg160_pm_ops = { >>>> + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) >>>> + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, >>>> + bmg160_runtime_resume, NULL) >>>> +}; >>>> + >>>> +static const struct acpi_device_id bmg160_acpi_match[] = { >>>> + {"BMG0160", 0}, >>>> + { }, >>>> +}; >>>> +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); >>>> + >>>> +static const struct i2c_device_id bmg160_id[] = { >>>> + {"bmg160", 0}, >>>> + {} >>>> +}; >>>> + >>>> +MODULE_DEVICE_TABLE(i2c, bmg160_id); >>>> + >>>> +static struct i2c_driver bmg160_driver = { >>>> + .driver = { >>>> + .name = BMG160_DRV_NAME, >>>> + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), >>>> + .pm = &bmg160_pm_ops, >>>> + }, >>>> + .probe = bmg160_probe, >>>> + .remove = bmg160_remove, >>>> + .id_table = bmg160_id, >>>> +}; >>>> +module_i2c_driver(bmg160_driver); >>>> + >>>> +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); >>>> +MODULE_LICENSE("GPL v2"); >>>> +MODULE_DESCRIPTION("BMG160 Gyro driver"); >>>> >>> >> > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/2] iio:gyro:bmg160 documentation 2014-08-05 21:57 [PATCH v3 0/2] iio:gyro:bmg160 Gyro Sensor driver Srinivas Pandruvada 2014-08-05 21:57 ` [PATCH v3 1/2] " Srinivas Pandruvada @ 2014-08-05 21:57 ` Srinivas Pandruvada 1 sibling, 0 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2014-08-05 21:57 UTC (permalink / raw) To: jic23; +Cc: linux-iio, Srinivas Pandruvada Added any-motion trigger documentation. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> --- Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 b/Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 new file mode 100644 index 0000000..e98209c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-gyro-bmg160 @@ -0,0 +1,7 @@ +What: /sys/bus/iio/devices/triggerX/name = "bmg160-any-motion-devX" +KernelVersion: 3.17 +Contact: linux-iio@vger.kernel.org +Description: + The BMG160 gyro kernel module provides an additional trigger, + which sets driver in a mode, where data is pushed to the buffer + only when there is any motion. -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-08-26 17:37 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-08-05 21:57 [PATCH v3 0/2] iio:gyro:bmg160 Gyro Sensor driver Srinivas Pandruvada 2014-08-05 21:57 ` [PATCH v3 1/2] " Srinivas Pandruvada 2014-08-07 10:39 ` Jonathan Cameron 2014-08-08 2:47 ` Srinivas Pandruvada 2014-08-12 19:06 ` Srinivas Pandruvada 2014-08-19 19:51 ` Jonathan Cameron 2014-08-26 17:37 ` Jonathan Cameron 2014-08-05 21:57 ` [PATCH v3 2/2] iio:gyro:bmg160 documentation Srinivas Pandruvada
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).