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: Jagath Jog J <jagathjog1996@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220505133021.22362-5-jagathjog1996@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Jagath Jog J 2022-05-05 19:00:15 +05:30 committed by Jonathan Cameron
parent 12c99f859f
commit ffe0ab6a96
5 changed files with 185 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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[] = {

View File

@ -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[] = {