Skip to content

Commit

Permalink
iio: accel: bma400: Add triggered buffer support
Browse files Browse the repository at this point in the history
Added trigger buffer support to read continuous acceleration
data from device with data ready interrupt which is mapped
to INT1 pin.

Signed-off-by: Jagath Jog J <jagathjog1996@gmail.com>
  • Loading branch information
embeddeddiaries authored and intel-lab-lkp committed Mar 19, 2022
1 parent d67d283 commit 8dc9a46
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 8 deletions.
2 changes: 2 additions & 0 deletions drivers/iio/accel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,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
Expand Down
10 changes: 9 additions & 1 deletion drivers/iio/accel/bma400.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -92,6 +99,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
161 changes: 156 additions & 5 deletions drivers/iio/accel/bma400_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>

#include "bma400.h"

Expand Down Expand Up @@ -61,6 +67,13 @@ struct bma400_data {
struct bma400_sample_freq sample_freq;
int oversampling_ratio;
int scale;
struct iio_trigger *trig;
/* Correct time stamp alignment */
struct {
__be16 buff[3];
u8 temperature;
s64 ts __aligned(8);
} buffer;
};

static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
Expand Down Expand Up @@ -152,7 +165,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, \
Expand All @@ -164,17 +177,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)
Expand Down Expand Up @@ -632,6 +660,11 @@ static int bma400_init(struct bma400_data *data)
if (ret)
goto err_reg_disable;

/* Configure INT-1 pin to push pull */
ret = regmap_write(data->regmap, BMA400_INT_IO_CTRL_REG, 0x02);
if (ret)
goto err_reg_disable;

/*
* Once the interrupt engine is supported we might use the
* data_src_reg, but for now ensure this is set to the
Expand Down Expand Up @@ -786,13 +819,97 @@ 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;

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

return 0;
}

static const unsigned long bma400_avail_scan_masks[] = {
GENMASK(3, 0),
0,
};

static const struct iio_info bma400_info = {
.read_raw = bma400_read_raw,
.read_avail = bma400_read_avail,
.write_raw = bma400_write_raw,
.write_raw_get_fmt = bma400_write_raw_get_fmt,
};

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;

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, 3 * sizeof(__be16));
mutex_unlock(&data->mutex);
if (ret)
goto out;

ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &temp);
if (ret)
goto out;

data->buffer.temperature = temp;

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

out:
iio_trigger_notify_done(indio_dev->trig);

return IRQ_HANDLED;
}

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;
__le16 status;

mutex_lock(&data->mutex);
ret = regmap_bulk_read(data->regmap, BMA400_INT_STAT0_REG, &status,
sizeof(status));
mutex_unlock(&data->mutex);
if (ret)
goto out;

if (status & BMA400_INT_DRDY_MSK)
iio_trigger_poll_chained(data->trig);

out:
return IRQ_HANDLED;
}

static void bma400_disable(void *data_ptr)
{
struct bma400_data *data = data_ptr;
Expand All @@ -806,7 +923,8 @@ static void bma400_disable(void *data_ptr)
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
}

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)
{
struct iio_dev *indio_dev;
struct bma400_data *data;
Expand All @@ -833,12 +951,45 @@ 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;

ret = devm_add_action_or_reset(dev, bma400_disable, data);
if (ret)
return ret;

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) {
dev_err(dev, "iio trigger register failed\n");
return ret;
}
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) {
dev_err(dev, "request irq %d failed\n", irq);
return ret;
}
}

ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
&bma400_trigger_handler, NULL);
if (ret) {
dev_err(dev, "iio triggered buffer setup failed\n");
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL(bma400_probe);
Expand Down
2 changes: 1 addition & 1 deletion drivers/iio/accel/bma400_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
2 changes: 1 addition & 1 deletion drivers/iio/accel/bma400_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down

0 comments on commit 8dc9a46

Please sign in to comment.