@@ -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,
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,
@@ -348,6 +381,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)
@@ -366,6 +401,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);
@@ -399,6 +436,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;
@@ -406,6 +497,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));
@@ -441,13 +534,15 @@ static int bmi270_get_data(struct bmi270_data *data,
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,
@@ -459,11 +554,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;
@@ -488,12 +583,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;
}
@@ -599,6 +703,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;
@@ -759,6 +973,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)
@@ -771,6 +987,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);
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. Signed-off-by: Gustavo Silva <gustavograzs@gmail.com> --- drivers/iio/imu/bmi270/bmi270_core.c | 234 +++++++++++++++++++++++++++++++++-- 1 file changed, 227 insertions(+), 7 deletions(-)