Re: [PATCH 1/2] iio: bmi160: Support hardware fifo

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

 



> This patch was developed primarily based on bmc150_accel hardware fifo
> implementation.

parts of the patch are cleanup and bugfixing; should be separate?

more comments below
 
> IRQ handler was added, which for now is responsible only for handling
> watermark interrupts. The BMI160 chip has two interrupt outputs. By
> default INT is considered to be connected. If INT2 is used instead, the
> interrupt-names device-tree property can be used to specify that.
> 
> Signed-off-by: Marcin Niestroj <m.niestroj@xxxxxxxxxxxxxxxx>
> ---
>  drivers/iio/imu/bmi160/bmi160.h      |   3 +-
>  drivers/iio/imu/bmi160/bmi160_core.c | 633 +++++++++++++++++++++++++++++++++--
>  drivers/iio/imu/bmi160/bmi160_i2c.c  |   7 +-
>  drivers/iio/imu/bmi160/bmi160_spi.c  |   3 +-
>  4 files changed, 618 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
> index d2ae6ed..4a7c10e 100644
> --- a/drivers/iio/imu/bmi160/bmi160.h
> +++ b/drivers/iio/imu/bmi160/bmi160.h
> @@ -4,7 +4,8 @@
>  extern const struct regmap_config bmi160_regmap_config;
>  
>  int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> -		      const char *name, bool use_spi);
> +		      const char *name, int irq,
> +		      bool use_spi, bool block_supported);
>  void bmi160_core_remove(struct device *dev);
>  
>  #endif  /* BMI160_H_ */
> diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
> index e0251b8..153734c 100644
> --- a/drivers/iio/imu/bmi160/bmi160_core.c
> +++ b/drivers/iio/imu/bmi160/bmi160_core.c
> @@ -2,6 +2,7 @@
>   * BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
>   *
>   * Copyright (c) 2016, Intel Corporation.
> + * Copyright (c) 2016, Grinn
>   *
>   * This file is subject to the terms and conditions of version 2 of
>   * the GNU General Public License.  See the file COPYING in the main
> @@ -9,7 +10,7 @@
>   *
>   * IIO core driver for BMI160, with support for I2C/SPI busses
>   *
> - * TODO: magnetometer, interrupts, hardware FIFO
> + * TODO: magnetometer, interrupts
>   */
>  #include <linux/module.h>
>  #include <linux/regmap.h>
> @@ -22,8 +23,12 @@
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/sysfs.h>
>  
> +#include <linux/of_irq.h>
> +
>  #include "bmi160.h"
>  
> +#define BMI160_IRQ_NAME		"bmi160_event"
> +
>  #define BMI160_REG_CHIP_ID	0x00
>  #define BMI160_CHIP_ID_VAL	0xD1
>  
> @@ -34,6 +39,21 @@
>  #define BMI160_REG_DATA_GYRO_XOUT_L	0x0C
>  #define BMI160_REG_DATA_ACCEL_XOUT_L	0x12
>  
> +#define BMI160_REG_STATUS		0x1B
> +#define BMI160_STATUS_MAG_MAN_OP	BIT(2)
> +
> +#define BMI160_REG_INT_STATUS0		0x1C
> +
> +#define BMI160_REG_INT_STATUS1		0x1D
> +#define BMI160_INT_STATUS_FWM		BIT(6)
> +
> +#define BMI160_REG_INT_STATUS2		0x1E
> +
> +#define BMI160_REG_INT_STATUS3		0x1F
> +
> +#define BMI160_REG_FIFO_LENGTH		0x22
> +#define BMI160_REG_FIFO_DATA		0x24
> +
>  #define BMI160_REG_ACCEL_CONFIG		0x40
>  #define BMI160_ACCEL_CONFIG_ODR_MASK	GENMASK(3, 0)
>  #define BMI160_ACCEL_CONFIG_BWP_MASK	GENMASK(6, 4)
> @@ -55,6 +75,36 @@
>  #define BMI160_GYRO_RANGE_250DPS	0x03
>  #define BMI160_GYRO_RANGE_125DPS	0x04
>  
> +#define BMI160_REG_FIFO_CONFIG_0	0x46
> +
> +#define BMI160_REG_FIFO_CONFIG_1	0x47
> +#define BMI160_FIFO_GYRO_EN		BIT(7)
> +#define BMI160_FIFO_ACCEL_EN		BIT(6)
> +#define BMI160_FIFO_MAGN_EN		BIT(5)
> +#define BMI160_FIFO_HEADER_EN		BIT(4)
> +#define BMI160_FIFO_TAG_INT1_EN		BIT(3)
> +#define BMI160_FIFO_TAG_INT2_EN		BIT(2)
> +#define BMI160_FIFO_TIME_EN		BIT(1)
> +
> +#define BMI160_REG_INT_EN_1		0x51
> +#define BMI160_INT_FWM_EN		BIT(6)
> +#define BMI160_INT_FFULL_EN		BIT(5)
> +#define BMI160_INT_DRDY_EN		BIT(4)
> +
> +#define BMI160_REG_INT_OUT_CTRL		0x53
> +#define BMI160_INT2_OUTPUT_EN		BIT(7)
> +#define BMI160_INT1_OUTPUT_EN		BIT(3)
> +
> +#define BMI160_REG_INT_LATCH		0x54
> +
> +#define BMI160_REG_INT_MAP_1		0x56
> +#define BMI160_INT1_MAP_DRDY		BIT(7)
> +#define BMI160_INT1_MAP_FWM		BIT(6)
> +#define BMI160_INT1_MAP_FFULL		BIT(5)
> +#define BMI160_INT2_MAP_DRDY		BIT(3)
> +#define BMI160_INT2_MAP_FWM		BIT(2)
> +#define BMI160_INT2_MAP_FFULL		BIT(1)
> +
>  #define BMI160_REG_CMD			0x7E
>  #define BMI160_CMD_ACCEL_PM_SUSPEND	0x10
>  #define BMI160_CMD_ACCEL_PM_NORMAL	0x11
> @@ -66,6 +116,8 @@
>  
>  #define BMI160_REG_DUMMY		0x7F
>  
> +#define BMI160_FIFO_LENGTH		1024
> +
>  #define BMI160_ACCEL_PMU_MIN_USLEEP	3200
>  #define BMI160_ACCEL_PMU_MAX_USLEEP	3800
>  #define BMI160_GYRO_PMU_MIN_USLEEP	55000
> @@ -110,8 +162,33 @@ enum bmi160_sensor_type {
>  	BMI160_NUM_SENSORS /* must be last */
>  };
>  
> +struct bmi160_irq_data {
> +	unsigned int map_fwm;
> +	unsigned int output_en;
> +};
> +
> +static const struct bmi160_irq_data bmi160_irq1_data = {
> +	.map_fwm = BMI160_INT1_MAP_FWM,
> +	.output_en = BMI160_INT1_OUTPUT_EN,
> +};
> +
> +static const struct bmi160_irq_data bmi160_irq2_data = {
> +	.map_fwm = BMI160_INT2_MAP_FWM,
> +	.output_en = BMI160_INT2_OUTPUT_EN,
> +};
> +
>  struct bmi160_data {
>  	struct regmap *regmap;
> +	struct mutex mutex;
> +	const struct bmi160_irq_data *irq_data;
> +	int irq;
> +	int64_t timestamp;
> +	int64_t fifo_sample_period;
> +	bool fifo_enabled;
> +	unsigned int fifo_config;
> +	unsigned int fifo_sample_size;
> +	u8 *fifo_buffer;
> +	unsigned int watermark;
>  };
>  
>  const struct regmap_config bmi160_regmap_config = {
> @@ -159,11 +236,11 @@ struct bmi160_pmu_time {
>  static struct bmi160_pmu_time bmi160_pmu_time[] = {
>  	[BMI160_ACCEL] = {
>  		.min = BMI160_ACCEL_PMU_MIN_USLEEP,
> -		.max = BMI160_ACCEL_PMU_MAX_USLEEP
> +		.max = BMI160_ACCEL_PMU_MAX_USLEEP,

this is cosmetic cleanup

>  	},
>  	[BMI160_GYRO] = {
>  		.min = BMI160_GYRO_PMU_MIN_USLEEP,
> -		.max = BMI160_GYRO_PMU_MIN_USLEEP,

this looks like bug fixing

> +		.max = BMI160_GYRO_PMU_MAX_USLEEP,
>  	},
>  };
>  
> @@ -285,7 +362,9 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
>  	else
>  		cmd = bmi160_regs[t].pmu_cmd_suspend;
>  
> +	mutex_lock(&data->mutex);

what does the mutex protect?
is it also needed without the fifo/irq support?
probably split out as a separate patch

>  	ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
> +	mutex_unlock(&data->mutex);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -298,6 +377,7 @@ static
>  int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
>  		     int uscale)
>  {
> +	int ret;
>  	int i;
>  
>  	for (i = 0; i < bmi160_scale_table[t].num; i++)
> @@ -307,8 +387,12 @@ int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
>  	if (i == bmi160_scale_table[t].num)
>  		return -EINVAL;
>  
> -	return regmap_write(data->regmap, bmi160_regs[t].range,
> -			    bmi160_scale_table[t].tbl[i].bits);
> +	mutex_lock(&data->mutex);
> +	ret = regmap_write(data->regmap, bmi160_regs[t].range,
> +			   bmi160_scale_table[t].tbl[i].bits);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
>  }
>  
>  static
> @@ -317,7 +401,9 @@ int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
>  {
>  	int i, ret, val;
>  
> +	mutex_lock(&data->mutex);
>  	ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
> +	mutex_unlock(&data->mutex);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -340,7 +426,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
>  
>  	reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
>  
> +	mutex_lock(&data->mutex);
>  	ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
> +	mutex_unlock(&data->mutex);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -353,6 +441,7 @@ static
>  int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
>  		   int odr, int uodr)
>  {
> +	int ret;
>  	int i;
>  
>  	for (i = 0; i < bmi160_odr_table[t].num; i++)
> @@ -363,20 +452,30 @@ int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
>  	if (i >= bmi160_odr_table[t].num)
>  		return -EINVAL;
>  
> -	return regmap_update_bits(data->regmap,
> -				  bmi160_regs[t].config,
> -				  bmi160_regs[t].config_odr_mask,
> -				  bmi160_odr_table[t].tbl[i].bits);
> +	mutex_lock(&data->mutex);
> +	ret = regmap_update_bits(data->regmap,
> +				 bmi160_regs[t].config,
> +				 bmi160_regs[t].config_odr_mask,
> +				 bmi160_odr_table[t].tbl[i].bits);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
>  }
>  
> -static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
> -			  int *odr, int *uodr)
> +static int64_t bmi160_frequency_to_period(int odr, int uodr)
>  {
> -	int i, val, ret;
> +	uint64_t period = 1000000000000000;
> +	int64_t frequency = (int64_t) odr * 1000000 + uodr;
>  
> -	ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
> -	if (ret < 0)
> -		return ret;
> +	do_div(period, frequency);
> +
> +	return period;
> +}
> +
> +static const struct bmi160_odr *bmi160_reg_to_odr(enum bmi160_sensor_type t,
> +						unsigned int val)
> +{
> +	int i;
>  
>  	val &= bmi160_regs[t].config_odr_mask;
>  
> @@ -385,10 +484,52 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
>  			break;
>  
>  	if (i >= bmi160_odr_table[t].num)
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
> +
> +	return &bmi160_odr_table[t].tbl[i];
> +}
> +
> +static int bmi160_get_sample_period(struct bmi160_data *data,
> +				enum bmi160_sensor_type t,
> +				int64_t *sample_period)
> +{
> +	const struct bmi160_odr *odr_entry;
> +	int ret;
> +	unsigned int val;
> +

no mutex here?

> +	ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
> +	if (ret < 0)
> +		return ret;
>  
> -	*odr = bmi160_odr_table[t].tbl[i].odr;
> -	*uodr = bmi160_odr_table[t].tbl[i].uodr;
> +	odr_entry = bmi160_reg_to_odr(t, val);
> +	if (IS_ERR(odr_entry))
> +		return PTR_ERR(odr_entry);
> +
> +	*sample_period = bmi160_frequency_to_period(odr_entry->odr,
> +						odr_entry->uodr);
> +
> +	return 0;
> +}
> +
> +static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
> +			  int *odr, int *uodr)
> +{
> +	const struct bmi160_odr *odr_entry;
> +	int ret;
> +	unsigned int val;
> +
> +	mutex_lock(&data->mutex);
> +	ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
> +	mutex_unlock(&data->mutex);
> +	if (ret < 0)
> +		return ret;
> +
> +	odr_entry = bmi160_reg_to_odr(t, val);
> +	if (IS_ERR(odr_entry))
> +		return PTR_ERR(odr_entry);
> +
> +	*odr = odr_entry->odr;
> +	*uodr = odr_entry->uodr;
>  
>  	return 0;
>  }
> @@ -402,14 +543,18 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
>  	int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
>  	__le16 sample;
>  
> +	mutex_lock(&data->mutex);
>  	for_each_set_bit(i, indio_dev->active_scan_mask,
>  			 indio_dev->masklength) {
>  		ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
>  				       &sample, sizeof(__le16));
> -		if (ret < 0)
> +		if (ret < 0) {
> +			mutex_unlock(&data->mutex);
>  			goto done;
> +		}
>  		buf[j++] = sample;
>  	}
> +	mutex_unlock(&data->mutex);
>  
>  	iio_push_to_buffers_with_timestamp(indio_dev, buf,
>  					   iio_get_time_ns(indio_dev));
> @@ -493,11 +638,364 @@ static const struct attribute_group bmi160_attrs_group = {
>  	.attrs = bmi160_attrs,
>  };
>  
> +static int bmi160_update_sample_period(struct bmi160_data *data,
> +				enum bmi160_sensor_type sensor_type)
> +{
> +	struct device *dev = regmap_get_device(data->regmap);
> +	int64_t sample_period;
> +	int ret;
> +
> +	ret = bmi160_get_sample_period(data, sensor_type, &sample_period);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (data->fifo_sample_period) {
> +		if (data->fifo_sample_period != sample_period) {
> +			dev_warn(dev, "Enabled sensors have unequal ODR values\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		data->fifo_sample_period = sample_period;

brackets not needed

> +	}
> +
> +	return 0;
> +}
> +
> +static int bmi160_fifo_enable(struct iio_dev *indio_dev,
> +			struct bmi160_data *data)
> +{
> +	struct regmap *regmap = data->regmap;
> +	struct device *dev = regmap_get_device(regmap);
> +	int ret;
> +	int i;
> +	unsigned int val;
> +	unsigned int fifo_config = 0;
> +
> +	/* Set fifo sample size and period */
> +	for_each_set_bit(i, indio_dev->active_scan_mask,
> +			indio_dev->masklength) {
> +		if (i <= BMI160_SCAN_GYRO_Z)
> +			fifo_config |= BMI160_FIFO_GYRO_EN;
> +		else if (i <= BMI160_SCAN_ACCEL_Z)
> +			fifo_config |= BMI160_FIFO_ACCEL_EN;
> +	}
> +
> +	data->fifo_sample_period = 0;
> +	data->fifo_sample_size = 0;
> +	if (fifo_config & BMI160_FIFO_GYRO_EN) {
> +		data->fifo_sample_size += 6;
> +		ret = bmi160_update_sample_period(data, BMI160_GYRO);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	if (fifo_config & BMI160_FIFO_ACCEL_EN) {
> +		data->fifo_sample_size += 6;
> +		ret = bmi160_update_sample_period(data, BMI160_ACCEL);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	/*
> +	 * Set watermark level and write real value back, as it will be used
> +	 * in timestamp calculation.
> +	 */
> +	val = data->watermark * data->fifo_sample_size;
> +	if (val > BMI160_FIFO_LENGTH - 1) {
> +		val = BMI160_FIFO_LENGTH - 1;
> +		data->watermark = val / data->fifo_sample_size;
> +	}
> +	val = data->watermark * data->fifo_sample_size / 4;
> +
> +	ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_0, val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set watermark\n");
> +		return ret;
> +	}
> +
> +	/* Enable FIFO channels */
> +	ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_1,
> +			fifo_config);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write FIFO_CONFIG_1\n");
> +		return ret;
> +	}
> +
> +	data->fifo_config = fifo_config;
> +	data->fifo_enabled = true;
> +
> +	return 0;
> +}
> +
> +static int bmi160_fifo_disable(struct bmi160_data *data)
> +{
> +	struct regmap *regmap = data->regmap;
> +	struct device *dev = regmap_get_device(regmap);
> +	int ret;
> +
> +	/* Disable all FIFO channels */
> +	ret = regmap_write(regmap, BMI160_REG_FIFO_CONFIG_1, 0);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write FIFO_CONFIG_1\n");
> +		return ret;
> +	}
> +
> +	data->fifo_enabled = false;
> +
> +	return 0;
> +}
> +
> +static int bmi160_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
> +		return iio_triggered_buffer_postenable(indio_dev);
> +
> +	mutex_lock(&data->mutex);
> +	ret = regmap_update_bits(data->regmap, BMI160_REG_INT_MAP_1,
> +			data->irq_data->map_fwm, data->irq_data->map_fwm);
> +	if (ret < 0)
> +		goto unlock;
> +
> +	ret = regmap_update_bits(data->regmap, BMI160_REG_INT_EN_1,
> +				BMI160_INT_FWM_EN, BMI160_INT_FWM_EN);
> +	if (ret < 0)
> +		goto unlock;
> +
> +	ret = bmi160_fifo_enable(indio_dev, data);
> +
> +unlock:
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
> +static int bmi160_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +	int ret = 0;
> +
> +	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
> +		return iio_triggered_buffer_predisable(indio_dev);
> +
> +	mutex_lock(&data->mutex);
> +
> +	ret = regmap_update_bits(regmap, BMI160_REG_INT_EN_1,
> +				BMI160_INT_FWM_EN, 0);
> +	if (ret < 0)
> +		goto unlock;
> +
> +	ret = bmi160_fifo_disable(data);
> +
> +unlock:
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops bmi160_buffer_ops = {
> +	.postenable = bmi160_buffer_postenable,
> +	.predisable = bmi160_buffer_predisable,
> +};
> +
> +static ssize_t bmi160_get_fifo_state(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	bool state;
> +
> +	mutex_lock(&data->mutex);
> +	state = data->fifo_enabled;
> +	mutex_unlock(&data->mutex);
> +
> +	return sprintf(buf, "%d\n", (int) state);
> +}
> +
> +static ssize_t bmi160_get_fifo_watermark(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	int wm;
> +
> +	mutex_lock(&data->mutex);
> +	wm = data->watermark;
> +	mutex_unlock(&data->mutex);
> +
> +	return sprintf(buf, "%d\n", wm);
> +}
> +
> +static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
> +static IIO_CONST_ATTR(hwfifo_watermark_max,
> +		      __stringify(BMI160_FIFO_LENGTH));
> +static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO,
> +		       bmi160_get_fifo_state, NULL, 0);
> +static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO,
> +		       bmi160_get_fifo_watermark, NULL, 0);
> +
> +static const struct attribute *bmi160_fifo_attributes[] = {
> +	&iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
> +	&iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
> +	&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
> +	&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
> +	NULL,
> +};
> +
> +static int bmi160_set_watermark(struct iio_dev *indio_dev, unsigned int val)
> +{
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +
> +	if (val > BMI160_FIFO_LENGTH)
> +		val = BMI160_FIFO_LENGTH;
> +
> +	data->watermark = val;
> +
> +	return 0;
> +}
> +
> +static int bmi160_fifo_transfer(struct bmi160_data *data,
> +				char *buffer, int num_bytes)
> +{
> +	struct regmap *regmap = data->regmap;
> +	struct device *dev = regmap_get_device(regmap);
> +	size_t step = regmap_get_raw_read_max(regmap);
> +	int ret = 0;
> +	int i;
> +
> +	if (!step || step > num_bytes)
> +		step = num_bytes;
> +	else if (step < num_bytes)
> +		step = data->fifo_sample_size;
> +
> +	for (i = 0; i < num_bytes; i += step) {
> +		ret = regmap_raw_read(regmap, BMI160_REG_FIFO_DATA,
> +				&buffer[i], step);
> +
> +		if (ret)
> +			break;
> +	}
> +
> +	if (ret)
> +		dev_err(dev,
> +			"Error transferring data from fifo in single steps of %zu\n",
> +			step);
> +
> +	return ret;
> +}
> +
> +static int __bmi160_fifo_flush(struct iio_dev *indio_dev,
> +			unsigned int samples, bool irq)

what does the __ prefix denote?
likely that the function is supposed to be called with mutex held -- 
maybe use that convention elsewhere as well?

> +{
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +	struct device *dev = regmap_get_device(regmap);
> +	int ret;
> +	__le16 fifo_length;
> +	unsigned int fifo_samples;
> +	unsigned int fifo_bytes;
> +	u8 *buffer = data->fifo_buffer;
> +	u8 *buffer_iter;
> +	int64_t last_timestamp, timestamp;
> +	unsigned int last_samples;
> +	unsigned int i;
> +
> +	/* Get the current FIFO length */
> +	ret = regmap_bulk_read(regmap, BMI160_REG_FIFO_LENGTH,
> +			&fifo_length, sizeof(__le16));
> +	if (ret < 0) {
> +		dev_err(dev, "Error reading FIFO_LENGTH\n");
> +		return ret;
> +	}
> +
> +	fifo_bytes = le16_to_cpu(fifo_length);
> +	fifo_samples = fifo_bytes / data->fifo_sample_size;
> +
> +	if (fifo_bytes % data->fifo_sample_size)
> +		dev_warn(dev, "fifo_bytes %u is not dividable by %u\n",
> +			fifo_bytes, data->fifo_sample_size);
> +
> +	if (!fifo_samples)
> +		return 0;
> +
> +	if (samples && fifo_samples > samples) {
> +		fifo_samples = samples;
> +		fifo_bytes = fifo_samples * data->fifo_sample_size;
> +	}
> +
> +	/*
> +	 * If we are not called from IRQ, it means that we are flushing data
> +	 * on demand. In that case we do not have latest timestamp saved in
> +	 * data->timestamp. Get the time now instead.
> +	 *
> +	 * In case of IRQ flush, saved timestamp shows the time when number
> +	 * of samples configured by watermark were ready. Currently there might
> +	 * be more samples already.
> +	 * If we are not called from IRQ, than we are getting the current fifo
> +	 * length, as we are setting timestamp just after getting it.
> +	 */
> +	if (!irq) {
> +		last_timestamp = iio_get_time_ns(indio_dev);
> +		last_samples = fifo_samples;
> +	} else {
> +		last_timestamp = data->timestamp;
> +		last_samples = data->watermark;
> +	}
> +
> +	/* Get all measurements */
> +	ret = bmi160_fifo_transfer(data, buffer, fifo_bytes);
> +	if (ret)
> +		return ret;
> +
> +	/* Handle demux */
> +	timestamp = last_timestamp - (last_samples * data->fifo_sample_period);
> +	buffer_iter = buffer;
> +	for (i = 0; i < fifo_samples; i++) {
> +		u8 tmp_buf[indio_dev->scan_bytes];

non-constant array size, is this allowed these days?

> +
> +		memcpy(tmp_buf, buffer_iter, data->fifo_sample_size);
> +
> +		timestamp += data->fifo_sample_period;
> +		iio_push_to_buffers_with_timestamp(indio_dev,
> +						tmp_buf,
> +						timestamp);
> +
> +		buffer_iter += data->fifo_sample_size;
> +	}
> +
> +	return fifo_samples;
> +}
> +
> +static int bmi160_fifo_flush(struct iio_dev *indio_dev, unsigned int samples)
> +{
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = __bmi160_fifo_flush(indio_dev, samples, false);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
>  static const struct iio_info bmi160_info = {
> -	.driver_module = THIS_MODULE,
> -	.read_raw = bmi160_read_raw,
> -	.write_raw = bmi160_write_raw,
> -	.attrs = &bmi160_attrs_group,
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= bmi160_read_raw,
> +	.write_raw		= bmi160_write_raw,
> +	.attrs			= &bmi160_attrs_group,
> +};
> +
> +static const struct iio_info bmi160_info_fifo = {
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= bmi160_read_raw,
> +	.write_raw		= bmi160_write_raw,
> +	.attrs			= &bmi160_attrs_group,
> +	.hwfifo_set_watermark	= bmi160_set_watermark,
> +	.hwfifo_flush_to_buffer	= bmi160_fifo_flush,
>  };
>  
>  static const char *bmi160_match_acpi_device(struct device *dev)
> @@ -561,12 +1059,54 @@ static void bmi160_chip_uninit(struct bmi160_data *data)
>  	bmi160_set_mode(data, BMI160_ACCEL, false);
>  }
>  
> +static int bmi160_enable_irq(struct bmi160_data *data)
> +{
> +	int ret;
> +
> +	mutex_lock(&data->mutex);
> +	ret = regmap_update_bits(data->regmap, BMI160_REG_INT_OUT_CTRL,
> +				data->irq_data->output_en,
> +				data->irq_data->output_en);
> +	mutex_unlock(&data->mutex);
> +
> +	return ret;
> +}
> +
> +static irqreturn_t bmi160_irq_thread_handler(int irq, void *p)
> +{
> +	struct iio_dev *indio_dev = p;
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +	struct device *dev = regmap_get_device(data->regmap);
> +
> +	mutex_lock(&data->mutex);
> +	if (data->fifo_enabled)
> +		__bmi160_fifo_flush(indio_dev, BMI160_FIFO_LENGTH, true);
> +	else
> +		dev_warn(dev,
> +			"IRQ has been triggered, but FIFO is not enabled.\n");
> +	mutex_unlock(&data->mutex);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bmi160_irq_handler(int irq, void *p)
> +{
> +	struct iio_dev *indio_dev = p;
> +	struct bmi160_data *data = iio_priv(indio_dev);
> +
> +	data->timestamp = iio_get_time_ns(indio_dev);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
>  int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> -		      const char *name, bool use_spi)
> +		const char *name, int irq,
> +		bool use_spi, bool block_supported)
>  {
>  	struct iio_dev *indio_dev;
>  	struct bmi160_data *data;
>  	int ret;
> +	int irq2;
>  
>  	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
>  	if (!indio_dev)
> @@ -574,8 +1114,11 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
>  
>  	data = iio_priv(indio_dev);
>  	dev_set_drvdata(dev, indio_dev);
> +	data->irq = irq;
>  	data->regmap = regmap;
>  
> +	mutex_init(&data->mutex);
> +
>  	ret = bmi160_chip_init(data, use_spi);
>  	if (ret < 0)
>  		return ret;
> @@ -591,10 +1134,50 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
>  	indio_dev->info = &bmi160_info;
>  
>  	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> -					 bmi160_trigger_handler, NULL);
> +					 bmi160_trigger_handler,
> +					 &bmi160_buffer_ops);
>  	if (ret < 0)
>  		goto uninit;
>  
> +	if (data->irq > 0) {
> +		/* Check which interrupt pin is connected to our board */
> +		irq2 = of_irq_get_byname(dev->of_node, "INT2");
> +		if (irq2 == data->irq) {
> +			dev_dbg(dev, "Using interrupt line INT2\n");
> +			data->irq_data = &bmi160_irq2_data;
> +		} else {
> +			dev_dbg(dev, "Using interrupt line INT1\n");
> +			data->irq_data = &bmi160_irq1_data;
> +		}
> +
> +		ret = devm_request_threaded_irq(dev,
> +						data->irq,
> +						bmi160_irq_handler,
> +						bmi160_irq_thread_handler,
> +						IRQF_ONESHOT,
> +						BMI160_IRQ_NAME,
> +						indio_dev);
> +		if (ret)
> +			return ret;
> +
> +		ret = bmi160_enable_irq(data);
> +		if (ret < 0)
> +			goto buffer_cleanup;
> +
> +		if (block_supported) {
> +			indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> +			indio_dev->info = &bmi160_info_fifo;
> +			indio_dev->buffer->attrs = bmi160_fifo_attributes;
> +			data->fifo_buffer = devm_kmalloc(dev,
> +							BMI160_FIFO_LENGTH,
> +							GFP_KERNEL);
> +			if (!data->fifo_buffer) {
> +				ret = -ENOMEM;

need to disable irq on failure?

> +				goto buffer_cleanup;
> +			}
> +		}
> +	}
> +
>  	ret = iio_device_register(indio_dev);
>  	if (ret < 0)
>  		goto buffer_cleanup;
> diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
> index 07a179d..aa63f89 100644
> --- a/drivers/iio/imu/bmi160/bmi160_i2c.c
> +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
> @@ -23,6 +23,10 @@ static int bmi160_i2c_probe(struct i2c_client *client,
>  {
>  	struct regmap *regmap;
>  	const char *name = NULL;
> +	bool block_supported =
> +		i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
> +		i2c_check_functionality(client->adapter,
> +					I2C_FUNC_SMBUS_READ_I2C_BLOCK);
>  
>  	regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config);
>  	if (IS_ERR(regmap)) {
> @@ -34,7 +38,8 @@ static int bmi160_i2c_probe(struct i2c_client *client,
>  	if (id)
>  		name = id->name;
>  
> -	return bmi160_core_probe(&client->dev, regmap, name, false);
> +	return bmi160_core_probe(&client->dev, regmap, name, client->irq,
> +				false, block_supported);
>  }
>  
>  static int bmi160_i2c_remove(struct i2c_client *client)
> diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
> index 1ec8b12..9b57fbe 100644
> --- a/drivers/iio/imu/bmi160/bmi160_spi.c
> +++ b/drivers/iio/imu/bmi160/bmi160_spi.c
> @@ -25,7 +25,8 @@ static int bmi160_spi_probe(struct spi_device *spi)
>  			(int)PTR_ERR(regmap));
>  		return PTR_ERR(regmap);
>  	}
> -	return bmi160_core_probe(&spi->dev, regmap, id->name, true);
> +	return bmi160_core_probe(&spi->dev, regmap, id->name, spi->irq,
> +				true, true);
>  }
>  
>  static int bmi160_spi_remove(struct spi_device *spi)
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
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



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux