Prepare the interrupt handler. Add register entries to evaluate the
incoming interrupt. Add functions to clear status registers and reset the
FIFO.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 16 ++++
drivers/iio/accel/adxl313_core.c | 131 +++++++++++++++++++++++++++++++
2 files changed, 147 insertions(+)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index c5673f1934fb..4cb1fe1f2616 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -49,11 +49,25 @@
#define ADXL313_SPI_3WIRE BIT(6)
#define ADXL313_I2C_DISABLE BIT(6)
+#define ADXL313_INT_OVERRUN BIT(0)
+#define ADXL313_INT_WATERMARK BIT(1)
+#define ADXL313_INT_INACTIVITY BIT(3)
+#define ADXL313_INT_ACTIVITY BIT(4)
+#define ADXL313_INT_DREADY BIT(7)
+
+/* FIFO entries: how many values are stored in the FIFO */
+#define ADXL313_REG_FIFO_STATUS_ENTRIES_MSK GENMASK(5, 0)
+/* FIFO samples: number of samples needed for watermark (FIFO mode) */
+#define ADXL313_REG_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0)
#define ADXL313_REG_FIFO_CTL_MODE_MSK GENMASK(7, 6)
#define ADXL313_FIFO_BYPASS 0
#define ADXL313_FIFO_STREAM 2
+#define ADXL313_FIFO_SIZE 32
+
+#define ADXL313_NUM_AXIS 3
+
extern const struct regmap_access_table adxl312_readable_regs_table;
extern const struct regmap_access_table adxl313_readable_regs_table;
extern const struct regmap_access_table adxl314_readable_regs_table;
@@ -75,7 +89,9 @@ struct adxl313_data {
const struct adxl313_chip_info *chip_info;
struct mutex lock; /* lock to protect transf_buf */
int irq;
+ u8 fifo_mode;
__le16 transf_buf __aligned(IIO_DMA_MINALIGN);
+ __le16 fifo_buf[ADXL313_NUM_AXIS * ADXL313_FIFO_SIZE + 1];
};
struct adxl313_chip_info {
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 52fb10680939..8eecd2851512 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -13,12 +13,20 @@
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+
#include "adxl313.h"
#define ADXL313_INT_NONE U8_MAX
#define ADXL313_INT1 1
#define ADXL313_INT2 2
+#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0)
+
static const struct regmap_range adxl312_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
@@ -62,6 +70,7 @@ bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg)
case ADXL313_REG_DATA_AXIS(4):
case ADXL313_REG_DATA_AXIS(5):
case ADXL313_REG_FIFO_STATUS:
+ case ADXL313_REG_INT_SOURCE:
return true;
default:
return false;
@@ -365,6 +374,116 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
+static int adxl313_get_samples(struct adxl313_data *data)
+{
+ unsigned int regval = 0;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_FIFO_STATUS, ®val);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(ADXL313_REG_FIFO_STATUS_ENTRIES_MSK, regval);
+}
+
+static int adxl313_set_fifo(struct adxl313_data *data)
+{
+ unsigned int int_line;
+ int ret;
+
+ ret = adxl313_set_measure_en(data, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_INT_MAP, &int_line);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
+ FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, data->fifo_mode));
+
+ return adxl313_set_measure_en(data, true);
+}
+
+static int adxl313_fifo_transfer(struct adxl313_data *data, int samples)
+{
+ size_t count;
+ int i;
+ int ret;
+
+ count = sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS;
+ for (i = 0; i < samples; i++) {
+ ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE,
+ data->fifo_buf + (i * count / 2), count);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void adxl313_fifo_reset(struct adxl313_data *data)
+{
+ int regval;
+ int samples;
+
+ adxl313_set_measure_en(data, false);
+
+ /* clear samples */
+ samples = adxl313_get_samples(data);
+ if (samples)
+ adxl313_fifo_transfer(data, samples);
+
+ /* clear interrupt register */
+ regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, ®val);
+
+ adxl313_set_measure_en(data, true);
+}
+
+static int adxl313_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+
+ data->fifo_mode = ADXL313_FIFO_STREAM;
+ return adxl313_set_fifo(data);
+}
+
+static int adxl313_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int ret;
+
+ data->fifo_mode = ADXL313_FIFO_BYPASS;
+ ret = adxl313_set_fifo(data);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0);
+}
+
+static const struct iio_buffer_setup_ops adxl313_buffer_ops = {
+ .postenable = adxl313_buffer_postenable,
+ .predisable = adxl313_buffer_predisable,
+};
+
+static irqreturn_t adxl313_irq_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int int_stat;
+
+ if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
+ return IRQ_NONE;
+
+ if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
+ goto err;
+
+ return IRQ_HANDLED;
+err:
+ adxl313_fifo_reset(data);
+
+ return IRQ_HANDLED;
+}
+
static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
@@ -486,6 +605,18 @@ int adxl313_core_probe(struct device *dev,
ret = regmap_write(data->regmap, ADXL313_REG_INT_MAP, regval);
if (ret)
return ret;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &adxl313_buffer_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, data->irq, NULL,
+ &adxl313_irq_handler,
+ IRQF_SHARED | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
} else {
/* FIFO_BYPASSED mode */
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
--
2.39.5
On Tue, May 20, 2025 at 10:50:02PM +0000, Lothar Rubusch wrote:
> Prepare the interrupt handler. Add register entries to evaluate the
> incoming interrupt. Add functions to clear status registers and reset the
> FIFO.
...
> +static int adxl313_fifo_transfer(struct adxl313_data *data, int samples)
> +{
> + size_t count;
> + int i;
Does it need to be signed?
> + int ret;
> +
> + count = sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS;
> + for (i = 0; i < samples; i++) {
> + ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE,
> + data->fifo_buf + (i * count / 2), count);
> + if (ret)
> + return ret;
> + }
> + return 0;
> +}
...
> +static void adxl313_fifo_reset(struct adxl313_data *data)
> +{
> + int regval;
Incorrect type. Please, go through your whole code and check that for regmap
returned value you use the same type. Using signed might lead to subtle and
hard-to-hunt mistakes due to integer promotion rules in C language.
> + int samples;
> +
> + adxl313_set_measure_en(data, false);
> +
> + /* clear samples */
> + samples = adxl313_get_samples(data);
> + if (samples)
> + adxl313_fifo_transfer(data, samples);
> +
> + /* clear interrupt register */
> + regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, ®val);
No error check? At least comment why it's okay to go like this.
> + adxl313_set_measure_en(data, true);
> +}
...
> +static irqreturn_t adxl313_irq_handler(int irq, void *p)
> +{
> + struct iio_dev *indio_dev = p;
> + struct adxl313_data *data = iio_priv(indio_dev);
> + int int_stat;
> +
> + if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
> + return IRQ_NONE;
> +
> + if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
> + goto err;
> +
> + return IRQ_HANDLED;
> +err:
> + adxl313_fifo_reset(data);
> +
> + return IRQ_HANDLED;
Seems you ignored my comment in previous review round. Please, re-read that
thread and discuss all the stuff you disagree with (I want to know why).
> +}
...
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
One space too many.
> + &adxl313_buffer_ops);
> + if (ret)
> + return ret;
--
With Best Regards,
Andy Shevchenko
© 2016 - 2025 Red Hat, Inc.