Provide an all-axes read for triggered buffering.

Signed-off-by: Eva Rachel Retuya <eraret...@gmail.com>
---
 drivers/iio/accel/Kconfig        |   2 +
 drivers/iio/accel/adxl345_core.c | 124 +++++++++++++++++++++++++++++++++++----
 drivers/iio/accel/adxl345_i2c.c  |   4 ++
 3 files changed, 119 insertions(+), 11 deletions(-)

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 15de262..45092da 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -7,6 +7,8 @@ menu "Accelerometers"
 
 config ADXL345
        tristate
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
 
 config ADXL345_I2C
        tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C 
Driver"
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 4b7055c..5df1e73 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -13,8 +13,11 @@
 #include <linux/module.h>
 #include <linux/regmap.h>
 
+#include <linux/iio/buffer.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 #include "adxl345.h"
 
@@ -54,11 +57,20 @@
  */
 static const int adxl345_uscale = 38300;
 
+enum adxl345_scan_index {
+       ADXL345_IDX_X,
+       ADXL345_IDX_Y,
+       ADXL345_IDX_Z,
+       ADXL345_IDX_TSTAMP,
+};
+
 struct adxl345_data {
        struct iio_trigger *drdy_trig;
        struct regmap *regmap;
+       struct mutex lock; /* protect this data structure */
        bool drdy_trig_on;
        u8 data_range;
+       s16 buffer[8]; /* 3 x 16-bit channels + padding + 64-bit timestamp */
 };
 
 static int adxl345_set_mode(struct adxl345_data *data, u8 mode)
@@ -75,12 +87,12 @@ static int adxl345_set_mode(struct adxl345_data *data, u8 
mode)
        return 0;
 }
 
-static int adxl345_get_triple(struct adxl345_data *data, void *buf)
+static int adxl345_get_triple(struct adxl345_data *data)
 {
        struct device *dev = regmap_get_device(data->regmap);
        int ret;
 
-       ret = regmap_bulk_read(data->regmap, ADXL345_REG_DATAX0, buf,
+       ret = regmap_bulk_read(data->regmap, ADXL345_REG_DATAX0, data->buffer,
                               sizeof(__le16) * 3);
        if (ret < 0) {
                dev_err(dev, "Failed to read the data registers, %d\n", ret);
@@ -120,12 +132,25 @@ static int adxl345_drdy(struct adxl345_data *data)
        .address = reg,                                                 \
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),           \
+       .scan_index = ADXL345_IDX_##axis,                               \
+       .scan_type = {                                                  \
+               .sign = 's',                                            \
+               .realbits = 13,                                         \
+               .storagebits = 16,                                      \
+               .endianness = IIO_LE,                                   \
+       },                                                              \
 }
 
 static const struct iio_chan_spec adxl345_channels[] = {
        ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
        ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
        ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
+       IIO_CHAN_SOFT_TIMESTAMP(ADXL345_IDX_TSTAMP),
+};
+
+static const unsigned long adxl345_scan_masks[] = {
+       BIT(ADXL345_IDX_X) | BIT(ADXL345_IDX_Y) | BIT(ADXL345_IDX_Z),
+       0
 };
 
 static int adxl345_read_raw(struct iio_dev *indio_dev,
@@ -133,31 +158,44 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
                            int *val, int *val2, long mask)
 {
        struct adxl345_data *data = iio_priv(indio_dev);
-       s16 regval[3];
        int ret;
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
+               ret = iio_device_claim_direct_mode(indio_dev);
+               if (ret)
+                       return ret;
+
+               mutex_lock(&data->lock);
                ret = adxl345_set_mode(data, ADXL345_POWER_CTL_MEASURE);
-               if (ret < 0)
+               if (ret < 0) {
+                       mutex_unlock(&data->lock);
                        return ret;
+               }
                ret = adxl345_drdy(data);
-               if (ret < 0)
+               if (ret < 0) {
+                       adxl345_set_mode(data, ADXL345_POWER_CTL_STANDBY);
+                       mutex_unlock(&data->lock);
                        return ret;
+               }
 
-               ret = adxl345_get_triple(data, &regval);
-               if (ret < 0)
+               ret = adxl345_get_triple(data);
+               mutex_unlock(&data->lock);
+               iio_device_release_direct_mode(indio_dev);
+               if (ret < 0) {
+                       adxl345_set_mode(data, ADXL345_POWER_CTL_STANDBY);
                        return ret;
+               }
 
                switch (chan->address) {
                case ADXL345_REG_DATAX0:
-                       *val = regval[0];
+                       *val = data->buffer[ADXL345_IDX_X];
                        break;
                case ADXL345_REG_DATAY0:
-                       *val = regval[1];
+                       *val = data->buffer[ADXL345_IDX_Y];
                        break;
                case ADXL345_REG_DATAZ0:
-                       *val = regval[2];
+                       *val = data->buffer[ADXL345_IDX_Z];
                        break;
                default:
                        return -EINVAL;
@@ -194,6 +232,56 @@ static irqreturn_t adxl345_irq(int irq, void *p)
        return ret;
 }
 
+static irqreturn_t adxl345_trigger_handler(int irq, void *p)
+{
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct adxl345_data *data = iio_priv(indio_dev);
+       int ret;
+
+       mutex_lock(&data->lock);
+       ret = adxl345_get_triple(data);
+       mutex_unlock(&data->lock);
+       if (ret < 0)
+               goto error;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+                                          pf->timestamp);
+error:
+       iio_trigger_notify_done(indio_dev->trig);
+
+       return IRQ_HANDLED;
+}
+
+static int adxl345_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+       struct adxl345_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = iio_triggered_buffer_postenable(indio_dev);
+       if (ret)
+               return ret;
+
+       return adxl345_set_mode(data, ADXL345_POWER_CTL_MEASURE);
+}
+
+static int adxl345_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+       struct adxl345_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = adxl345_set_mode(data, ADXL345_POWER_CTL_STANDBY);
+       if (ret)
+               return ret;
+
+       return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops adxl345_buffer_setup_ops = {
+       .postenable = adxl345_triggered_buffer_postenable,
+       .predisable = adxl345_triggered_buffer_predisable,
+};
+
 static int adxl345_drdy_trigger_set_state(struct iio_trigger *trig, bool state)
 {
        struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
@@ -278,12 +366,15 @@ int adxl345_core_probe(struct device *dev, struct regmap 
*regmap, int irq,
                return ret;
        }
 
+       mutex_init(&data->lock);
+
        indio_dev->dev.parent = dev;
        indio_dev->name = name;
        indio_dev->info = &adxl345_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->channels = adxl345_channels;
        indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
+       indio_dev->available_scan_masks = adxl345_scan_masks;
 
        if (irq > 0) {
                ret =
@@ -312,14 +403,24 @@ int adxl345_core_probe(struct device *dev, struct regmap 
*regmap, int irq,
                }
        }
 
+       ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+                                        adxl345_trigger_handler,
+                                        &adxl345_buffer_setup_ops);
+       if (ret < 0) {
+               dev_err(dev, "iio_triggered_buffer_setup failed: %d\n", ret);
+               goto err_trigger_unregister;
+       }
+
        ret = iio_device_register(indio_dev);
        if (ret < 0) {
                dev_err(dev, "iio_device_register failed: %d\n", ret);
-               goto err_trigger_unregister;
+               goto err_buffer_cleanup;
        }
 
        return ret;
 
+err_buffer_cleanup:
+       iio_triggered_buffer_cleanup(indio_dev);
 err_trigger_unregister:
        if (data->drdy_trig)
                iio_trigger_unregister(data->drdy_trig);
@@ -334,6 +435,7 @@ int adxl345_core_remove(struct device *dev)
        struct adxl345_data *data = iio_priv(indio_dev);
 
        iio_device_unregister(indio_dev);
+       iio_triggered_buffer_cleanup(indio_dev);
        if (data->drdy_trig)
                iio_trigger_unregister(data->drdy_trig);
 
diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c
index 8c791b8..1e0f071 100644
--- a/drivers/iio/accel/adxl345_i2c.c
+++ b/drivers/iio/accel/adxl345_i2c.c
@@ -30,6 +30,10 @@ static int adxl345_i2c_probe(struct i2c_client *client,
        bool use_int2 = false;
        int irq;
 
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+               return -EOPNOTSUPP;
+
        regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
        if (IS_ERR(regmap)) {
                dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
-- 
2.7.4

Reply via email to