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@xxxxxxxxxxxxxxx> >> 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@xxxxxxxxxxxxxxx>"); >>> +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@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html