iio: accel: bma400: Add triggered buffer support
authorJagath Jog J <jagathjog1996@gmail.com>
Thu, 5 May 2022 13:30:15 +0000 (19:00 +0530)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sat, 11 Jun 2022 13:35:26 +0000 (14:35 +0100)
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>
drivers/iio/accel/Kconfig
drivers/iio/accel/bma400.h
drivers/iio/accel/bma400_core.c
drivers/iio/accel/bma400_i2c.c
drivers/iio/accel/bma400_spi.c

index b53f010..3579871 100644 (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
index 1c8c47a..907e1a6 100644 (file)
 #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
 
 
 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
index 07674d8..67e102c 100644 (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>
 #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);
index 4f6e01a..1ba2a98 100644 (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[] = {
index 28e2404..ec13c04 100644 (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[] = {