Commit ffe0ab6a authored by Jagath Jog J's avatar Jagath Jog J Committed by Jonathan Cameron
Browse files

iio: accel: bma400: Add triggered buffer support



Added trigger buffer support to read continuous acceleration
and temperature data from device with data ready interrupt which
is mapped to INT1 pin.

Signed-off-by: default avatarJagath Jog J <jagathjog1996@gmail.com>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220505133021.22362-5-jagathjog1996@gmail.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 12c99f85
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -204,6 +204,8 @@ config BMA220
config BMA400
	tristate "Bosch BMA400 3-Axis Accelerometer Driver"
	select REGMAP
	select IIO_BUFFER
	select IIO_TRIGGERED_BUFFER
	select BMA400_I2C if I2C
	select BMA400_SPI if SPI
	help
+9 −1
Original line number Diff line number Diff line
@@ -62,6 +62,13 @@
#define BMA400_ACC_CONFIG2_REG      0x1b
#define BMA400_CMD_REG              0x7e

/* Interrupt registers */
#define BMA400_INT_CONFIG0_REG	    0x1f
#define BMA400_INT_CONFIG1_REG	    0x20
#define BMA400_INT1_MAP_REG	    0x21
#define BMA400_INT_IO_CTRL_REG	    0x24
#define BMA400_INT_DRDY_MSK	    BIT(7)

/* Chip ID of BMA 400 devices found in the chip ID register. */
#define BMA400_ID_REG_VAL           0x90

@@ -111,6 +118,7 @@

extern const struct regmap_config bma400_regmap_config;

int bma400_probe(struct device *dev, struct regmap *regmap, const char *name);
int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
		 const char *name);

#endif
+172 −5
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
 *  - Create channel for sensor time
 */

#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -20,6 +21,10 @@
#include <linux/regulator/consumer.h>

#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

#include "bma400.h"

@@ -46,6 +51,13 @@ enum bma400_power_mode {
	POWER_MODE_INVALID = 0x03,
};

enum bma400_scan {
	BMA400_ACCL_X,
	BMA400_ACCL_Y,
	BMA400_ACCL_Z,
	BMA400_TEMP,
};

struct bma400_sample_freq {
	int hz;
	int uhz;
@@ -61,6 +73,14 @@ struct bma400_data {
	struct bma400_sample_freq sample_freq;
	int oversampling_ratio;
	int scale;
	struct iio_trigger *trig;
	/* Correct time stamp alignment */
	struct {
		__le16 buff[3];
		u8 temperature;
		s64 ts __aligned(8);
	} buffer __aligned(IIO_ALIGN);
	__le16 status;
};

static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
@@ -152,7 +172,7 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
	{ }
};

#define BMA400_ACC_CHANNEL(_axis) { \
#define BMA400_ACC_CHANNEL(_index, _axis) { \
	.type = IIO_ACCEL, \
	.modified = 1, \
	.channel2 = IIO_MOD_##_axis, \
@@ -164,17 +184,32 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
		BIT(IIO_CHAN_INFO_SCALE) | \
		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
	.ext_info = bma400_ext_info, \
	.scan_index = _index,	\
	.scan_type = {		\
		.sign = 's',	\
		.realbits = 12,		\
		.storagebits = 16,	\
		.endianness = IIO_LE,	\
	},				\
}

static const struct iio_chan_spec bma400_channels[] = {
	BMA400_ACC_CHANNEL(X),
	BMA400_ACC_CHANNEL(Y),
	BMA400_ACC_CHANNEL(Z),
	BMA400_ACC_CHANNEL(0, X),
	BMA400_ACC_CHANNEL(1, Y),
	BMA400_ACC_CHANNEL(2, Z),
	{
		.type = IIO_TEMP,
		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
		.scan_index = 3,
		.scan_type = {
			.sign = 's',
			.realbits = 8,
			.storagebits = 8,
			.endianness = IIO_LE,
		},
	},
	IIO_CHAN_SOFT_TIMESTAMP(4),
};

static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2)
@@ -659,6 +694,10 @@ static int bma400_init(struct bma400_data *data)
	if (ret)
		return ret;

	/* Configure INT1 pin to open drain */
	ret = regmap_write(data->regmap, BMA400_INT_IO_CTRL_REG, 0x06);
	if (ret)
		return ret;
	/*
	 * Once the interrupt engine is supported we might use the
	 * data_src_reg, but for now ensure this is set to the
@@ -807,6 +846,31 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
	}
}

static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
					     bool state)
{
	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
	struct bma400_data *data = iio_priv(indio_dev);
	int ret;

	ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG,
				 BMA400_INT_DRDY_MSK,
				 FIELD_PREP(BMA400_INT_DRDY_MSK, state));
	if (ret)
		return ret;

	return regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG,
				  BMA400_INT_DRDY_MSK,
				  FIELD_PREP(BMA400_INT_DRDY_MSK, state));
}

static const unsigned long bma400_avail_scan_masks[] = {
	BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z),
	BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z)
	| BIT(BMA400_TEMP),
	0
};

static const struct iio_info bma400_info = {
	.read_raw          = bma400_read_raw,
	.read_avail        = bma400_read_avail,
@@ -814,7 +878,78 @@ static const struct iio_info bma400_info = {
	.write_raw_get_fmt = bma400_write_raw_get_fmt,
};

int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
static const struct iio_trigger_ops bma400_trigger_ops = {
	.set_trigger_state = &bma400_data_rdy_trigger_set_state,
	.validate_device = &iio_trigger_validate_own_device,
};

static irqreturn_t bma400_trigger_handler(int irq, void *p)
{
	struct iio_poll_func *pf = p;
	struct iio_dev *indio_dev = pf->indio_dev;
	struct bma400_data *data = iio_priv(indio_dev);
	int ret, temp;

	/* Lock to protect the data->buffer */
	mutex_lock(&data->mutex);

	/* bulk read six registers, with the base being the LSB register */
	ret = regmap_bulk_read(data->regmap, BMA400_X_AXIS_LSB_REG,
			       &data->buffer.buff, sizeof(data->buffer.buff));
	if (ret)
		goto unlock_err;

	if (test_bit(BMA400_TEMP, indio_dev->active_scan_mask)) {
		ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &temp);
		if (ret)
			goto unlock_err;

		data->buffer.temperature = temp;
	}

	iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
					   iio_get_time_ns(indio_dev));

	mutex_unlock(&data->mutex);
	iio_trigger_notify_done(indio_dev->trig);
	return IRQ_HANDLED;

unlock_err:
	mutex_unlock(&data->mutex);
	return IRQ_NONE;
}

static irqreturn_t bma400_interrupt(int irq, void *private)
{
	struct iio_dev *indio_dev = private;
	struct bma400_data *data = iio_priv(indio_dev);
	int ret;

	/* Lock to protect the data->status */
	mutex_lock(&data->mutex);
	ret = regmap_bulk_read(data->regmap, BMA400_INT_STAT0_REG,
			       &data->status,
			       sizeof(data->status));
	/*
	 * if none of the bit is set in the status register then it is
	 * spurious interrupt.
	 */
	if (ret || !data->status)
		goto unlock_err;

	if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
		mutex_unlock(&data->mutex);
		iio_trigger_poll_chained(data->trig);
		return IRQ_HANDLED;
	}

unlock_err:
	mutex_unlock(&data->mutex);
	return IRQ_NONE;
}

int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
		 const char *name)
{
	struct iio_dev *indio_dev;
	struct bma400_data *data;
@@ -841,8 +976,40 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
	indio_dev->info = &bma400_info;
	indio_dev->channels = bma400_channels;
	indio_dev->num_channels = ARRAY_SIZE(bma400_channels);
	indio_dev->available_scan_masks = bma400_avail_scan_masks;
	indio_dev->modes = INDIO_DIRECT_MODE;

	if (irq > 0) {
		data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
						    indio_dev->name,
						    iio_device_id(indio_dev));
		if (!data->trig)
			return -ENOMEM;

		data->trig->ops = &bma400_trigger_ops;
		iio_trigger_set_drvdata(data->trig, indio_dev);

		ret = devm_iio_trigger_register(data->dev, data->trig);
		if (ret)
			return dev_err_probe(data->dev, ret,
					     "iio trigger register fail\n");

		indio_dev->trig = iio_trigger_get(data->trig);
		ret = devm_request_threaded_irq(dev, irq, NULL,
						&bma400_interrupt,
						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
						indio_dev->name, indio_dev);
		if (ret)
			return dev_err_probe(data->dev, ret,
					     "request irq %d failed\n", irq);
	}

	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
					      &bma400_trigger_handler, NULL);
	if (ret)
		return dev_err_probe(data->dev, ret,
				     "iio triggered buffer setup failed\n");

	return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ static int bma400_i2c_probe(struct i2c_client *client,
		return PTR_ERR(regmap);
	}

	return bma400_probe(&client->dev, regmap, id->name);
	return bma400_probe(&client->dev, regmap, client->irq, id->name);
}

static const struct i2c_device_id bma400_i2c_ids[] = {
+1 −1
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ static int bma400_spi_probe(struct spi_device *spi)
	if (ret)
		dev_err(&spi->dev, "Failed to read chip id register\n");

	return bma400_probe(&spi->dev, regmap, id->name);
	return bma400_probe(&spi->dev, regmap, spi->irq, id->name);
}

static const struct spi_device_id bma400_spi_ids[] = {