mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-04 04:28:10 -04:00
iio: imu: bmi270: add support for data ready interrupt trigger
The BMI270 sensor provides two interrupt pins that can be used for different interrupt sources, including a data ready signal. Add support for configuring one the pins as a trigger source. The interrupt pin can be configured with various options: active high or low, push-pull or open-drain, and latched or non-latched. Acked-by: Alex Lanzano <lanzano.alex@gmail.com> Signed-off-by: Gustavo Silva <gustavograzs@gmail.com> Link: https://patch.msgid.link/20250228-bmi270-irq-v2-3-3f97a4e8f551@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
committed by
Jonathan Cameron
parent
16c94de2aa
commit
a1854d55f5
@@ -4,11 +4,13 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
@@ -26,6 +28,9 @@
|
||||
#define BMI270_ACCEL_X_REG 0x0c
|
||||
#define BMI270_ANG_VEL_X_REG 0x12
|
||||
|
||||
#define BMI270_INT_STATUS_1_REG 0x1d
|
||||
#define BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK GENMASK(7, 6)
|
||||
|
||||
#define BMI270_INTERNAL_STATUS_REG 0x21
|
||||
#define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0)
|
||||
#define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01
|
||||
@@ -55,6 +60,20 @@
|
||||
#define BMI270_GYR_CONF_RANGE_REG 0x43
|
||||
#define BMI270_GYR_CONF_RANGE_MSK GENMASK(2, 0)
|
||||
|
||||
#define BMI270_INT1_IO_CTRL_REG 0x53
|
||||
#define BMI270_INT2_IO_CTRL_REG 0x54
|
||||
#define BMI270_INT_IO_CTRL_LVL_MSK BIT(1)
|
||||
#define BMI270_INT_IO_CTRL_OD_MSK BIT(2)
|
||||
#define BMI270_INT_IO_CTRL_OP_MSK BIT(3)
|
||||
#define BMI270_INT_IO_LVL_OD_OP_MSK GENMASK(3, 1)
|
||||
|
||||
#define BMI270_INT_LATCH_REG 0x55
|
||||
#define BMI270_INT_LATCH_REG_MSK BIT(0)
|
||||
|
||||
#define BMI270_INT_MAP_DATA_REG 0x58
|
||||
#define BMI270_INT_MAP_DATA_DRDY_INT1_MSK BIT(2)
|
||||
#define BMI270_INT_MAP_DATA_DRDY_INT2_MSK BIT(6)
|
||||
|
||||
#define BMI270_INIT_CTRL_REG 0x59
|
||||
#define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0)
|
||||
|
||||
@@ -78,10 +97,20 @@
|
||||
#define BMI260_INIT_DATA_FILE "bmi260-init-data.fw"
|
||||
#define BMI270_INIT_DATA_FILE "bmi270-init-data.fw"
|
||||
|
||||
enum bmi270_irq_pin {
|
||||
BMI270_IRQ_DISABLED,
|
||||
BMI270_IRQ_INT1,
|
||||
BMI270_IRQ_INT2,
|
||||
};
|
||||
|
||||
struct bmi270_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
const struct bmi270_chip_info *chip_info;
|
||||
enum bmi270_irq_pin irq_pin;
|
||||
struct iio_trigger *trig;
|
||||
/* Protect device's private data from concurrent access */
|
||||
struct mutex mutex;
|
||||
|
||||
/*
|
||||
* Where IIO_DMA_MINALIGN may be larger than 8 bytes, align to
|
||||
@@ -274,6 +303,8 @@ static int bmi270_set_scale(struct bmi270_data *data, int chan_type, int uscale)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
for (i = 0; i < bmi270_scale_item.num; i++) {
|
||||
if (bmi270_scale_item.tbl[i].uscale != uscale)
|
||||
continue;
|
||||
@@ -291,6 +322,8 @@ static int bmi270_get_scale(struct bmi270_data *data, int chan_type, int *scale,
|
||||
unsigned int val;
|
||||
struct bmi270_scale_item bmi270_scale_item;
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
switch (chan_type) {
|
||||
case IIO_ACCEL:
|
||||
ret = regmap_read(data->regmap, BMI270_ACC_CONF_RANGE_REG, &val);
|
||||
@@ -346,6 +379,8 @@ static int bmi270_set_odr(struct bmi270_data *data, int chan_type, int odr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
for (i = 0; i < bmi270_odr_item.num; i++) {
|
||||
if (bmi270_odr_item.tbl[i].odr != odr ||
|
||||
bmi270_odr_item.tbl[i].uodr != uodr)
|
||||
@@ -364,6 +399,8 @@ static int bmi270_get_odr(struct bmi270_data *data, int chan_type, int *odr,
|
||||
int i, val, ret;
|
||||
struct bmi270_odr_item bmi270_odr_item;
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
switch (chan_type) {
|
||||
case IIO_ACCEL:
|
||||
ret = regmap_read(data->regmap, BMI270_ACC_CONF_REG, &val);
|
||||
@@ -397,6 +434,60 @@ static int bmi270_get_odr(struct bmi270_data *data, int chan_type, int *odr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t bmi270_irq_thread_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct bmi270_data *data = iio_priv(indio_dev);
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
scoped_guard(mutex, &data->mutex) {
|
||||
ret = regmap_read(data->regmap, BMI270_INT_STATUS_1_REG,
|
||||
&status);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (FIELD_GET(BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK, status))
|
||||
iio_trigger_poll_nested(data->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bmi270_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct bmi270_data *data = iio_trigger_get_drvdata(trig);
|
||||
unsigned int field_value = 0;
|
||||
unsigned int mask;
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
switch (data->irq_pin) {
|
||||
case BMI270_IRQ_INT1:
|
||||
mask = BMI270_INT_MAP_DATA_DRDY_INT1_MSK;
|
||||
set_mask_bits(&field_value, BMI270_INT_MAP_DATA_DRDY_INT1_MSK,
|
||||
FIELD_PREP(BMI270_INT_MAP_DATA_DRDY_INT1_MSK,
|
||||
state));
|
||||
break;
|
||||
case BMI270_IRQ_INT2:
|
||||
mask = BMI270_INT_MAP_DATA_DRDY_INT2_MSK;
|
||||
set_mask_bits(&field_value, BMI270_INT_MAP_DATA_DRDY_INT2_MSK,
|
||||
FIELD_PREP(BMI270_INT_MAP_DATA_DRDY_INT2_MSK,
|
||||
state));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(data->regmap, BMI270_INT_MAP_DATA_REG, mask,
|
||||
field_value);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops bmi270_trigger_ops = {
|
||||
.set_trigger_state = &bmi270_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
static irqreturn_t bmi270_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
@@ -404,6 +495,8 @@ static irqreturn_t bmi270_trigger_handler(int irq, void *p)
|
||||
struct bmi270_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMI270_ACCEL_X_REG,
|
||||
&data->buffer.channels,
|
||||
sizeof(data->buffer.channels));
|
||||
@@ -439,13 +532,15 @@ static int bmi270_get_data(struct bmi270_data *data, int chan_type, int axis,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(sample), 15);
|
||||
|
||||
return 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int bmi270_read_raw(struct iio_dev *indio_dev,
|
||||
@@ -457,11 +552,11 @@ static int bmi270_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = bmi270_get_data(data, chan->type, chan->channel2, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = bmi270_get_scale(data, chan->type, val, val2);
|
||||
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
@@ -486,12 +581,21 @@ static int bmi270_write_raw(struct iio_dev *indio_dev,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct bmi270_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return bmi270_set_scale(data, chan->type, val2);
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = bmi270_set_scale(data, chan->type, val2);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return bmi270_set_odr(data, chan->type, val, val2);
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = bmi270_set_odr(data, chan->type, val, val2);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -597,6 +701,116 @@ static const struct iio_chan_spec bmi270_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(BMI270_SCAN_TIMESTAMP),
|
||||
};
|
||||
|
||||
static int bmi270_int_pin_config(struct bmi270_data *data,
|
||||
enum bmi270_irq_pin irq_pin,
|
||||
bool active_high, bool open_drain, bool latch)
|
||||
{
|
||||
unsigned int reg, field_value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, BMI270_INT_LATCH_REG,
|
||||
BMI270_INT_LATCH_REG_MSK,
|
||||
FIELD_PREP(BMI270_INT_LATCH_REG_MSK, latch));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (irq_pin) {
|
||||
case BMI270_IRQ_INT1:
|
||||
reg = BMI270_INT1_IO_CTRL_REG;
|
||||
break;
|
||||
case BMI270_IRQ_INT2:
|
||||
reg = BMI270_INT2_IO_CTRL_REG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
field_value = FIELD_PREP(BMI270_INT_IO_CTRL_LVL_MSK, active_high) |
|
||||
FIELD_PREP(BMI270_INT_IO_CTRL_OD_MSK, open_drain) |
|
||||
FIELD_PREP(BMI270_INT_IO_CTRL_OP_MSK, 1);
|
||||
return regmap_update_bits(data->regmap, reg,
|
||||
BMI270_INT_IO_LVL_OD_OP_MSK, field_value);
|
||||
}
|
||||
|
||||
static int bmi270_trigger_probe(struct bmi270_data *data,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
bool open_drain, active_high, latch;
|
||||
struct fwnode_handle *fwnode;
|
||||
enum bmi270_irq_pin irq_pin;
|
||||
int ret, irq, irq_type;
|
||||
|
||||
fwnode = dev_fwnode(data->dev);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
irq = fwnode_irq_get_byname(fwnode, "INT1");
|
||||
if (irq > 0) {
|
||||
irq_pin = BMI270_IRQ_INT1;
|
||||
} else {
|
||||
irq = fwnode_irq_get_byname(fwnode, "INT2");
|
||||
if (irq < 0)
|
||||
return 0;
|
||||
|
||||
irq_pin = BMI270_IRQ_INT2;
|
||||
}
|
||||
|
||||
irq_type = irq_get_trigger_type(irq);
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_RISING:
|
||||
latch = false;
|
||||
active_high = true;
|
||||
break;
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
latch = true;
|
||||
active_high = true;
|
||||
break;
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
latch = false;
|
||||
active_high = false;
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
latch = true;
|
||||
active_high = false;
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(data->dev, -EINVAL,
|
||||
"Invalid interrupt type 0x%x specified\n",
|
||||
irq_type);
|
||||
}
|
||||
|
||||
open_drain = fwnode_property_read_bool(fwnode, "drive-open-drain");
|
||||
|
||||
ret = bmi270_int_pin_config(data, irq_pin, active_high, open_drain,
|
||||
latch);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret,
|
||||
"Failed to configure irq line\n");
|
||||
|
||||
data->trig = devm_iio_trigger_alloc(data->dev, "%s-trig-%d",
|
||||
indio_dev->name, irq_pin);
|
||||
if (!data->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
data->trig->ops = &bmi270_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->trig, data);
|
||||
|
||||
ret = devm_request_threaded_irq(data->dev, irq, NULL,
|
||||
bmi270_irq_thread_handler,
|
||||
IRQF_ONESHOT, "bmi270-int", indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Failed to request IRQ\n");
|
||||
|
||||
ret = devm_iio_trigger_register(data->dev, data->trig);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret,
|
||||
"Trigger registration failed\n");
|
||||
|
||||
data->irq_pin = irq_pin;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmi270_validate_chip_id(struct bmi270_data *data)
|
||||
{
|
||||
int chip_id;
|
||||
@@ -757,6 +971,8 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap,
|
||||
data->dev = dev;
|
||||
data->regmap = regmap;
|
||||
data->chip_info = chip_info;
|
||||
data->irq_pin = BMI270_IRQ_DISABLED;
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
ret = bmi270_chip_init(data);
|
||||
if (ret)
|
||||
@@ -769,6 +985,10 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmi270_info;
|
||||
|
||||
ret = bmi270_trigger_probe(data, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
bmi270_trigger_handler, NULL);
|
||||
|
||||
Reference in New Issue
Block a user