Message ID | 20241225181338.69672-7-l.rubusch@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | iio: accel: adxl345: add FIFO operating with IRQ triggered watermark events | expand |
On Wed, 25 Dec 2024 18:13:37 +0000 Lothar Rubusch <l.rubusch@gmail.com> wrote: > Add a basic setup for FIFO with configurable watermark. Add a handler > for watermark interrupt events and extend the channel for the > scan_index needed for the iio channel. The sensor is configurable to use > a FIFO_BYPASSED mode or a FIFO_STREAM mode. For the FIFO_STREAM mode now > a watermark can be configured, or disabled by setting 0. Further features > require a working FIFO setup. > > Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> > --- > drivers/iio/accel/adxl345.h | 27 ++- > drivers/iio/accel/adxl345_core.c | 305 ++++++++++++++++++++++++++++++- > 2 files changed, 321 insertions(+), 11 deletions(-) > > diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h > index 6f39f16d3..bf9e86cff 100644 > --- a/drivers/iio/accel/adxl345.h > +++ b/drivers/iio/accel/adxl345.h > @@ -15,18 +15,32 @@ > #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) > #define ADXL345_REG_BW_RATE 0x2C > #define ADXL345_REG_POWER_CTL 0x2D > +#define ADXL345_REG_INT_ENABLE 0x2E > +#define ADXL345_REG_INT_MAP 0x2F > +#define ADXL345_REG_INT_SOURCE 0x30 > +#define ADXL345_REG_INT_SOURCE_MSK 0xFF > #define ADXL345_REG_DATA_FORMAT 0x31 > -#define ADXL345_REG_DATAX0 0x32 > -#define ADXL345_REG_DATAY0 0x34 > -#define ADXL345_REG_DATAZ0 0x36 > -#define ADXL345_REG_DATA_AXIS(index) \ > - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) > +#define ADXL345_REG_XYZ_BASE 0x32 > +#define ADXL345_REG_DATA_AXIS(index) \ > + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) > > +#define ADXL345_REG_FIFO_CTL 0x38 > +#define ADXL345_REG_FIFO_STATUS 0x39 > +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F > + > +#define ADXL345_FIFO_CTL_SAMPLES(x) FIELD_PREP(GENMASK(4, 0), x) These need linux/bitfield.h to be included. However, that got me looking closer at this and it should be done differently. Just define the masks in here and put the FIELD_PREP() inline in the c file. Same for the various other FIELD_PREP macros in here. It may seem convenient to wrap all this up here, but in general I'd rather see that these are simple FIELD_PREP() calls where they are used inline. Thanks, Jonathan > +/* 0: INT1, 1: INT2 */ > +#define ADXL345_FIFO_CTL_TRIGGER(x) FIELD_PREP(BIT(5), x) > +#define ADXL345_FIFO_CTL_MODE(x) FIELD_PREP(GENMASK(7, 6), x) > + > +#define ADXL345_INT_DATA_READY BIT(7) > +#define ADXL345_INT_WATERMARK BIT(1) > +#define ADXL345_INT_OVERRUN BIT(0) > #define ADXL345_BW_RATE GENMASK(3, 0) > #define ADXL345_BASE_RATE_NANO_HZ 97656250LL > > -#define ADXL345_POWER_CTL_MEASURE BIT(3) > #define ADXL345_POWER_CTL_STANDBY 0x00 > +#define ADXL345_POWER_CTL_MEASURE BIT(3) > > #define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ > #define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ > @@ -40,6 +54,7 @@ > #define ADXL345_DATA_FORMAT_16G 3 > > #define ADXL345_DEVID 0xE5 > +#define ADXL345_FIFO_SIZE 32 > > /* > * In full-resolution mode, scale factor is maintained at ~4 mg/LSB > diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c > index 987a0fe03..738960298 100644 > --- a/drivers/iio/accel/adxl345_core.c > +++ b/drivers/iio/accel/adxl345_core.c > @@ -15,9 +15,17 @@ > > #include <linux/iio/iio.h> > #include <linux/iio/sysfs.h> > +#include <linux/iio/buffer.h> > +#include <linux/iio/kfifo_buf.h> > > #include "adxl345.h" > > +#define ADXL345_FIFO_BYPASS 0 > +#define ADXL345_FIFO_FIFO 1 > +#define ADXL345_FIFO_STREAM 2 > + > +#define ADXL345_DIRS 3 > + > #define ADXL345_INT_NONE 0xff > #define ADXL345_INT1 0 > #define ADXL345_INT2 1 > @@ -28,25 +36,66 @@ struct adxl345_state { > bool fifo_delay; /* delay: delay is needed for SPI */ > int irq; > u8 intio; > + u8 int_map; > + u8 watermark; > + u8 fifo_mode; > + __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); > }; > > -#define ADXL345_CHANNEL(index, axis) { \ > +#define ADXL345_CHANNEL(index, reg, axis) { \ > .type = IIO_ACCEL, \ > .modified = 1, \ > .channel2 = IIO_MOD_##axis, \ > - .address = index, \ > + .address = (reg), \ > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ > BIT(IIO_CHAN_INFO_CALIBBIAS), \ > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ > BIT(IIO_CHAN_INFO_SAMP_FREQ), \ > + .scan_index = (index), \ > + .scan_type = { \ > + .sign = 's', \ > + .realbits = 13, \ > + .storagebits = 16, \ > + .endianness = IIO_LE, \ > + }, \ > } > > +enum adxl345_chans { > + chan_x, chan_y, chan_z, > +}; > + > static const struct iio_chan_spec adxl345_channels[] = { > - ADXL345_CHANNEL(0, X), > - ADXL345_CHANNEL(1, Y), > - ADXL345_CHANNEL(2, Z), > + ADXL345_CHANNEL(0, chan_x, X), > + ADXL345_CHANNEL(1, chan_y, Y), > + ADXL345_CHANNEL(2, chan_z, Z), > }; > > +static const unsigned long adxl345_scan_masks[] = { > + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), > + 0 > +}; > + > +static int adxl345_set_interrupts(struct adxl345_state *st) > +{ > + int ret; > + unsigned int int_enable = st->int_map; > + unsigned int int_map; > + > + /* > + * Any bits set to 0 in the INT map register send their respective > + * interrupts to the INT1 pin, whereas bits set to 1 send their respective > + * interrupts to the INT2 pin. The intio shall convert this accordingly. > + */ > + int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, > + st->intio ? st->int_map : ~st->int_map); > + > + ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map); > + if (ret) > + return ret; > + > + return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, int_enable); > +} > + > static int adxl345_read_raw(struct iio_dev *indio_dev, > struct iio_chan_spec const *chan, > int *val, int *val2, long mask) > @@ -132,6 +181,24 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, > return -EINVAL; > } > > +static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value) > +{ > + struct adxl345_state *st = iio_priv(indio_dev); > + unsigned int fifo_mask = 0x1F; > + int ret; > + > + value = min(value, ADXL345_FIFO_SIZE - 1); > + > + ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value); > + if (ret) > + return ret; > + > + st->watermark = value; > + st->int_map |= ADXL345_INT_WATERMARK; > + > + return 0; > +} > + > static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, > struct iio_chan_spec const *chan, > long mask) > @@ -186,11 +253,217 @@ static const struct attribute_group adxl345_attrs_group = { > .attrs = adxl345_attrs, > }; > > +static int adxl345_set_fifo(struct adxl345_state *st) > +{ > + int ret; > + > + /* FIFO should only be configured while in standby mode */ > + ret = adxl345_set_measure_en(st, false); > + if (ret < 0) > + return ret; > + > + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, > + ADXL345_FIFO_CTL_SAMPLES(st->watermark) | > + ADXL345_FIFO_CTL_TRIGGER(st->intio) | > + ADXL345_FIFO_CTL_MODE(st->fifo_mode)); > + if (ret < 0) > + return ret; > + > + return adxl345_set_measure_en(st, true); > +} > + > +/** > + * adxl345_get_samples() - Read number of FIFO entries. > + * @st: The initialized state instance of this driver. > + * > + * The sensor does not support treating any axis individually, or exclude them > + * from measuring. > + * > + * Return: negative error, or value. > + */ > +static int adxl345_get_samples(struct adxl345_state *st) > +{ > + unsigned int regval = 0; > + int ret; > + > + ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, ®val); > + if (ret < 0) > + return ret; > + > + return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval); > +} > + > +/** > + * adxl345_fifo_transfer() - Read samples number of elements. > + * @st: The instance of the state object of this sensor. > + * @samples: The number of lines in the FIFO referred to as fifo_entry. > + * > + * It is recommended that a multiple-byte read of all registers be performed to > + * prevent a change in data between reads of sequential registers. That is to > + * read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once. > + * > + * Return: 0 or error value. > + */ > +static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) > +{ > + size_t count; > + int i, ret = 0; > + > + /* count is the 3x the fifo_buf element size, hence 6B */ > + count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; > + for (i = 0; i < samples; i++) { > + /* read 3x 2 byte elements from base address into next fifo_buf position */ > + ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, > + st->fifo_buf + (i * count / 2), count); > + if (ret < 0) > + return ret; > + > + /* > + * To ensure that the FIFO has completely popped, there must be at least 5 > + * us between the end of reading the data registers, signified by the > + * transition to register 0x38 from 0x37 or the CS pin going high, and the > + * start of new reads of the FIFO or reading the FIFO_STATUS register. For > + * SPI operation at 1.5 MHz or lower, the register addressing portion of the > + * transmission is sufficient delay to ensure the FIFO has completely > + * popped. It is necessary for SPI operation greater than 1.5 MHz to > + * de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us > + * at 5 MHz operation. > + */ > + if (st->fifo_delay && samples > 1) > + udelay(3); > + } > + return ret; > +} > + > +/** > + * adxl345_fifo_reset() - Empty the FIFO in error condition. > + * @st: The instance to the state object of the sensor. > + * > + * Read all elements of the FIFO. Reading the interrupt source register > + * resets the sensor. > + */ > +static void adxl345_fifo_reset(struct adxl345_state *st) > +{ > + int regval; > + int samples; > + > + adxl345_set_measure_en(st, false); > + > + samples = adxl345_get_samples(st); > + if (samples > 0) > + adxl345_fifo_transfer(st, samples); > + > + regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); > + > + adxl345_set_measure_en(st, true); > +} > + > +static int adxl345_buffer_postenable(struct iio_dev *indio_dev) > +{ > + struct adxl345_state *st = iio_priv(indio_dev); > + int ret; > + > + ret = adxl345_set_interrupts(st); > + if (ret < 0) > + return ret; > + > + st->fifo_mode = ADXL345_FIFO_STREAM; > + return adxl345_set_fifo(st); > +} > + > +static int adxl345_buffer_predisable(struct iio_dev *indio_dev) > +{ > + struct adxl345_state *st = iio_priv(indio_dev); > + int ret; > + > + st->fifo_mode = ADXL345_FIFO_BYPASS; > + ret = adxl345_set_fifo(st); > + if (ret < 0) > + return ret; > + > + st->int_map = 0x00; > + return adxl345_set_interrupts(st); > +} > + > +static const struct iio_buffer_setup_ops adxl345_buffer_ops = { > + .postenable = adxl345_buffer_postenable, > + .predisable = adxl345_buffer_predisable, > +}; > + > +static int adxl345_get_status(struct adxl345_state *st) > +{ > + int ret; > + unsigned int regval; > + > + ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); > + if (ret < 0) > + return ret; > + > + return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval); > +} > + > +static int adxl345_fifo_push(struct iio_dev *indio_dev, > + int samples) > +{ > + struct adxl345_state *st = iio_priv(indio_dev); > + int i, ret; > + > + if (samples <= 0) > + return -EINVAL; > + > + ret = adxl345_fifo_transfer(st, samples); > + if (ret) > + return ret; > + > + for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS) > + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); > + > + return 0; > +} > + > +/** > + * adxl345_irq_handler() - Handle irqs of the ADXL345. > + * @irq: The irq being handled. > + * @p: The struct iio_device pointer for the device. > + * > + * Return: The interrupt was handled. > + */ > +static irqreturn_t adxl345_irq_handler(int irq, void *p) > +{ > + struct iio_dev *indio_dev = p; > + struct adxl345_state *st = iio_priv(indio_dev); > + int int_stat; > + int samples; > + > + int_stat = adxl345_get_status(st); > + if (int_stat <= 0) > + return IRQ_NONE; > + > + if (int_stat & ADXL345_INT_OVERRUN) > + goto err; > + > + if (int_stat & ADXL345_INT_WATERMARK) { > + samples = adxl345_get_samples(st); > + if (samples < 0) > + goto err; > + > + if (adxl345_fifo_push(indio_dev, samples) < 0) > + goto err; > + } > + return IRQ_HANDLED; > + > +err: > + adxl345_fifo_reset(st); > + > + return IRQ_HANDLED; > +} > + > static const struct iio_info adxl345_info = { > .attrs = &adxl345_attrs_group, > .read_raw = adxl345_read_raw, > .write_raw = adxl345_write_raw, > .write_raw_get_fmt = adxl345_write_raw_get_fmt, > + .hwfifo_set_watermark = adxl345_set_watermark, > }; > > /** > @@ -221,6 +494,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > ADXL345_DATA_FORMAT_JUSTIFY | > ADXL345_DATA_FORMAT_FULL_RES | > ADXL345_DATA_FORMAT_SELF_TEST); > + u8 fifo_ctl; > int ret; > > indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); > @@ -239,6 +513,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > 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 (setup) { > /* Perform optional initial bus specific configuration */ > @@ -289,6 +564,26 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > st->intio = ADXL345_INT_NONE; > } > > + if (st->intio != ADXL345_INT_NONE) { > + /* FIFO_STREAM mode is going to be activated later */ > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); > + if (ret) > + return ret; > + > + ret = devm_request_threaded_irq(dev, st->irq, NULL, > + &adxl345_irq_handler, > + IRQF_SHARED | IRQF_ONESHOT, > + indio_dev->name, indio_dev); > + if (ret) > + return ret; > + } else { > + /* FIFO_BYPASS mode */ > + fifo_ctl = ADXL345_FIFO_CTL_MODE(ADXL345_FIFO_BYPASS); > + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, fifo_ctl); > + if (ret < 0) > + return ret; > + } > + > return devm_iio_device_register(dev, indio_dev); > } > EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345);
On Sat, Dec 28, 2024 at 3:45 PM Jonathan Cameron <jic23@kernel.org> wrote: > > On Wed, 25 Dec 2024 18:13:37 +0000 > Lothar Rubusch <l.rubusch@gmail.com> wrote: > > > Add a basic setup for FIFO with configurable watermark. Add a handler > > for watermark interrupt events and extend the channel for the > > scan_index needed for the iio channel. The sensor is configurable to use > > a FIFO_BYPASSED mode or a FIFO_STREAM mode. For the FIFO_STREAM mode now > > a watermark can be configured, or disabled by setting 0. Further features > > require a working FIFO setup. > > > > Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> > > --- > > drivers/iio/accel/adxl345.h | 27 ++- > > drivers/iio/accel/adxl345_core.c | 305 ++++++++++++++++++++++++++++++- > > 2 files changed, 321 insertions(+), 11 deletions(-) > > > > diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h > > index 6f39f16d3..bf9e86cff 100644 > > --- a/drivers/iio/accel/adxl345.h > > +++ b/drivers/iio/accel/adxl345.h > > @@ -15,18 +15,32 @@ > > #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) > > #define ADXL345_REG_BW_RATE 0x2C > > #define ADXL345_REG_POWER_CTL 0x2D > > +#define ADXL345_REG_INT_ENABLE 0x2E > > +#define ADXL345_REG_INT_MAP 0x2F > > +#define ADXL345_REG_INT_SOURCE 0x30 > > +#define ADXL345_REG_INT_SOURCE_MSK 0xFF > > #define ADXL345_REG_DATA_FORMAT 0x31 > > -#define ADXL345_REG_DATAX0 0x32 > > -#define ADXL345_REG_DATAY0 0x34 > > -#define ADXL345_REG_DATAZ0 0x36 > > -#define ADXL345_REG_DATA_AXIS(index) \ > > - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) > > +#define ADXL345_REG_XYZ_BASE 0x32 > > +#define ADXL345_REG_DATA_AXIS(index) \ > > + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) > > > > +#define ADXL345_REG_FIFO_CTL 0x38 > > +#define ADXL345_REG_FIFO_STATUS 0x39 > > +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F > > + > > +#define ADXL345_FIFO_CTL_SAMPLES(x) FIELD_PREP(GENMASK(4, 0), x) > These need linux/bitfield.h to be included. > Sure, I can do this. > However, that got me looking closer at this and it should be done > differently. > > Just define the masks in here and put the FIELD_PREP() inline in the > c file. Same for the various other FIELD_PREP macros in here. > > It may seem convenient to wrap all this up here, but in general > I'd rather see that these are simple FIELD_PREP() calls where they > are used inline. > Ok, I understand, the masks stay in the adxl345.h and the FIELD_RPEP() macros better go to where they are used, i.e. the adxl345_core.c. Thank you for reviewing this. I was a bit unsure about using the FIELD_PREP() / GENMASK() combinations correctly. The code seems to work for me, though. For future usage of those macros: Generally my usage of those macros is correct? Do I understand correctly, on the long run it would generally be cleaner, also to migrate the defines to the adxl345_core.c? IMHO, probably most of the constants in the adxl345.h can be private to adxl345_core.c, and probably better should. Best regards, L > Thanks, > > Jonathan > > > +/* 0: INT1, 1: INT2 */ > > +#define ADXL345_FIFO_CTL_TRIGGER(x) FIELD_PREP(BIT(5), x) > > +#define ADXL345_FIFO_CTL_MODE(x) FIELD_PREP(GENMASK(7, 6), x) > > + > > +#define ADXL345_INT_DATA_READY BIT(7) > > +#define ADXL345_INT_WATERMARK BIT(1) > > +#define ADXL345_INT_OVERRUN BIT(0) > > #define ADXL345_BW_RATE GENMASK(3, 0) > > #define ADXL345_BASE_RATE_NANO_HZ 97656250LL > > > > -#define ADXL345_POWER_CTL_MEASURE BIT(3) > > #define ADXL345_POWER_CTL_STANDBY 0x00 > > +#define ADXL345_POWER_CTL_MEASURE BIT(3) > > > > #define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ > > #define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ > > @@ -40,6 +54,7 @@ > > #define ADXL345_DATA_FORMAT_16G 3 > > > > #define ADXL345_DEVID 0xE5 > > +#define ADXL345_FIFO_SIZE 32 > > > > /* > > * In full-resolution mode, scale factor is maintained at ~4 mg/LSB > > diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c > > index 987a0fe03..738960298 100644 > > --- a/drivers/iio/accel/adxl345_core.c > > +++ b/drivers/iio/accel/adxl345_core.c > > @@ -15,9 +15,17 @@ > > > > #include <linux/iio/iio.h> > > #include <linux/iio/sysfs.h> > > +#include <linux/iio/buffer.h> > > +#include <linux/iio/kfifo_buf.h> > > > > #include "adxl345.h" > > > > +#define ADXL345_FIFO_BYPASS 0 > > +#define ADXL345_FIFO_FIFO 1 > > +#define ADXL345_FIFO_STREAM 2 > > + > > +#define ADXL345_DIRS 3 > > + > > #define ADXL345_INT_NONE 0xff > > #define ADXL345_INT1 0 > > #define ADXL345_INT2 1 > > @@ -28,25 +36,66 @@ struct adxl345_state { > > bool fifo_delay; /* delay: delay is needed for SPI */ > > int irq; > > u8 intio; > > + u8 int_map; > > + u8 watermark; > > + u8 fifo_mode; > > + __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); > > }; > > > > -#define ADXL345_CHANNEL(index, axis) { \ > > +#define ADXL345_CHANNEL(index, reg, axis) { \ > > .type = IIO_ACCEL, \ > > .modified = 1, \ > > .channel2 = IIO_MOD_##axis, \ > > - .address = index, \ > > + .address = (reg), \ > > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ > > BIT(IIO_CHAN_INFO_CALIBBIAS), \ > > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ > > BIT(IIO_CHAN_INFO_SAMP_FREQ), \ > > + .scan_index = (index), \ > > + .scan_type = { \ > > + .sign = 's', \ > > + .realbits = 13, \ > > + .storagebits = 16, \ > > + .endianness = IIO_LE, \ > > + }, \ > > } > > > > +enum adxl345_chans { > > + chan_x, chan_y, chan_z, > > +}; > > + > > static const struct iio_chan_spec adxl345_channels[] = { > > - ADXL345_CHANNEL(0, X), > > - ADXL345_CHANNEL(1, Y), > > - ADXL345_CHANNEL(2, Z), > > + ADXL345_CHANNEL(0, chan_x, X), > > + ADXL345_CHANNEL(1, chan_y, Y), > > + ADXL345_CHANNEL(2, chan_z, Z), > > }; > > > > +static const unsigned long adxl345_scan_masks[] = { > > + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), > > + 0 > > +}; > > + > > +static int adxl345_set_interrupts(struct adxl345_state *st) > > +{ > > + int ret; > > + unsigned int int_enable = st->int_map; > > + unsigned int int_map; > > + > > + /* > > + * Any bits set to 0 in the INT map register send their respective > > + * interrupts to the INT1 pin, whereas bits set to 1 send their respective > > + * interrupts to the INT2 pin. The intio shall convert this accordingly. > > + */ > > + int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, > > + st->intio ? st->int_map : ~st->int_map); > > + > > + ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map); > > + if (ret) > > + return ret; > > + > > + return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, int_enable); > > +} > > + > > static int adxl345_read_raw(struct iio_dev *indio_dev, > > struct iio_chan_spec const *chan, > > int *val, int *val2, long mask) > > @@ -132,6 +181,24 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, > > return -EINVAL; > > } > > > > +static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value) > > +{ > > + struct adxl345_state *st = iio_priv(indio_dev); > > + unsigned int fifo_mask = 0x1F; > > + int ret; > > + > > + value = min(value, ADXL345_FIFO_SIZE - 1); > > + > > + ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value); > > + if (ret) > > + return ret; > > + > > + st->watermark = value; > > + st->int_map |= ADXL345_INT_WATERMARK; > > + > > + return 0; > > +} > > + > > static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, > > struct iio_chan_spec const *chan, > > long mask) > > @@ -186,11 +253,217 @@ static const struct attribute_group adxl345_attrs_group = { > > .attrs = adxl345_attrs, > > }; > > > > +static int adxl345_set_fifo(struct adxl345_state *st) > > +{ > > + int ret; > > + > > + /* FIFO should only be configured while in standby mode */ > > + ret = adxl345_set_measure_en(st, false); > > + if (ret < 0) > > + return ret; > > + > > + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, > > + ADXL345_FIFO_CTL_SAMPLES(st->watermark) | > > + ADXL345_FIFO_CTL_TRIGGER(st->intio) | > > + ADXL345_FIFO_CTL_MODE(st->fifo_mode)); > > + if (ret < 0) > > + return ret; > > + > > + return adxl345_set_measure_en(st, true); > > +} > > + > > +/** > > + * adxl345_get_samples() - Read number of FIFO entries. > > + * @st: The initialized state instance of this driver. > > + * > > + * The sensor does not support treating any axis individually, or exclude them > > + * from measuring. > > + * > > + * Return: negative error, or value. > > + */ > > +static int adxl345_get_samples(struct adxl345_state *st) > > +{ > > + unsigned int regval = 0; > > + int ret; > > + > > + ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, ®val); > > + if (ret < 0) > > + return ret; > > + > > + return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval); > > +} > > + > > +/** > > + * adxl345_fifo_transfer() - Read samples number of elements. > > + * @st: The instance of the state object of this sensor. > > + * @samples: The number of lines in the FIFO referred to as fifo_entry. > > + * > > + * It is recommended that a multiple-byte read of all registers be performed to > > + * prevent a change in data between reads of sequential registers. That is to > > + * read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once. > > + * > > + * Return: 0 or error value. > > + */ > > +static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) > > +{ > > + size_t count; > > + int i, ret = 0; > > + > > + /* count is the 3x the fifo_buf element size, hence 6B */ > > + count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; > > + for (i = 0; i < samples; i++) { > > + /* read 3x 2 byte elements from base address into next fifo_buf position */ > > + ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, > > + st->fifo_buf + (i * count / 2), count); > > + if (ret < 0) > > + return ret; > > + > > + /* > > + * To ensure that the FIFO has completely popped, there must be at least 5 > > + * us between the end of reading the data registers, signified by the > > + * transition to register 0x38 from 0x37 or the CS pin going high, and the > > + * start of new reads of the FIFO or reading the FIFO_STATUS register. For > > + * SPI operation at 1.5 MHz or lower, the register addressing portion of the > > + * transmission is sufficient delay to ensure the FIFO has completely > > + * popped. It is necessary for SPI operation greater than 1.5 MHz to > > + * de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us > > + * at 5 MHz operation. > > + */ > > + if (st->fifo_delay && samples > 1) > > + udelay(3); > > + } > > + return ret; > > +} > > + > > +/** > > + * adxl345_fifo_reset() - Empty the FIFO in error condition. > > + * @st: The instance to the state object of the sensor. > > + * > > + * Read all elements of the FIFO. Reading the interrupt source register > > + * resets the sensor. > > + */ > > +static void adxl345_fifo_reset(struct adxl345_state *st) > > +{ > > + int regval; > > + int samples; > > + > > + adxl345_set_measure_en(st, false); > > + > > + samples = adxl345_get_samples(st); > > + if (samples > 0) > > + adxl345_fifo_transfer(st, samples); > > + > > + regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); > > + > > + adxl345_set_measure_en(st, true); > > +} > > + > > +static int adxl345_buffer_postenable(struct iio_dev *indio_dev) > > +{ > > + struct adxl345_state *st = iio_priv(indio_dev); > > + int ret; > > + > > + ret = adxl345_set_interrupts(st); > > + if (ret < 0) > > + return ret; > > + > > + st->fifo_mode = ADXL345_FIFO_STREAM; > > + return adxl345_set_fifo(st); > > +} > > + > > +static int adxl345_buffer_predisable(struct iio_dev *indio_dev) > > +{ > > + struct adxl345_state *st = iio_priv(indio_dev); > > + int ret; > > + > > + st->fifo_mode = ADXL345_FIFO_BYPASS; > > + ret = adxl345_set_fifo(st); > > + if (ret < 0) > > + return ret; > > + > > + st->int_map = 0x00; > > + return adxl345_set_interrupts(st); > > +} > > + > > +static const struct iio_buffer_setup_ops adxl345_buffer_ops = { > > + .postenable = adxl345_buffer_postenable, > > + .predisable = adxl345_buffer_predisable, > > +}; > > + > > +static int adxl345_get_status(struct adxl345_state *st) > > +{ > > + int ret; > > + unsigned int regval; > > + > > + ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); > > + if (ret < 0) > > + return ret; > > + > > + return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval); > > +} > > + > > +static int adxl345_fifo_push(struct iio_dev *indio_dev, > > + int samples) > > +{ > > + struct adxl345_state *st = iio_priv(indio_dev); > > + int i, ret; > > + > > + if (samples <= 0) > > + return -EINVAL; > > + > > + ret = adxl345_fifo_transfer(st, samples); > > + if (ret) > > + return ret; > > + > > + for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS) > > + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); > > + > > + return 0; > > +} > > + > > +/** > > + * adxl345_irq_handler() - Handle irqs of the ADXL345. > > + * @irq: The irq being handled. > > + * @p: The struct iio_device pointer for the device. > > + * > > + * Return: The interrupt was handled. > > + */ > > +static irqreturn_t adxl345_irq_handler(int irq, void *p) > > +{ > > + struct iio_dev *indio_dev = p; > > + struct adxl345_state *st = iio_priv(indio_dev); > > + int int_stat; > > + int samples; > > + > > + int_stat = adxl345_get_status(st); > > + if (int_stat <= 0) > > + return IRQ_NONE; > > + > > + if (int_stat & ADXL345_INT_OVERRUN) > > + goto err; > > + > > + if (int_stat & ADXL345_INT_WATERMARK) { > > + samples = adxl345_get_samples(st); > > + if (samples < 0) > > + goto err; > > + > > + if (adxl345_fifo_push(indio_dev, samples) < 0) > > + goto err; > > + } > > + return IRQ_HANDLED; > > + > > +err: > > + adxl345_fifo_reset(st); > > + > > + return IRQ_HANDLED; > > +} > > + > > static const struct iio_info adxl345_info = { > > .attrs = &adxl345_attrs_group, > > .read_raw = adxl345_read_raw, > > .write_raw = adxl345_write_raw, > > .write_raw_get_fmt = adxl345_write_raw_get_fmt, > > + .hwfifo_set_watermark = adxl345_set_watermark, > > }; > > > > /** > > @@ -221,6 +494,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > > ADXL345_DATA_FORMAT_JUSTIFY | > > ADXL345_DATA_FORMAT_FULL_RES | > > ADXL345_DATA_FORMAT_SELF_TEST); > > + u8 fifo_ctl; > > int ret; > > > > indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); > > @@ -239,6 +513,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > > 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 (setup) { > > /* Perform optional initial bus specific configuration */ > > @@ -289,6 +564,26 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, > > st->intio = ADXL345_INT_NONE; > > } > > > > + if (st->intio != ADXL345_INT_NONE) { > > + /* FIFO_STREAM mode is going to be activated later */ > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); > > + if (ret) > > + return ret; > > + > > + ret = devm_request_threaded_irq(dev, st->irq, NULL, > > + &adxl345_irq_handler, > > + IRQF_SHARED | IRQF_ONESHOT, > > + indio_dev->name, indio_dev); > > + if (ret) > > + return ret; > > + } else { > > + /* FIFO_BYPASS mode */ > > + fifo_ctl = ADXL345_FIFO_CTL_MODE(ADXL345_FIFO_BYPASS); > > + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, fifo_ctl); > > + if (ret < 0) > > + return ret; > > + } > > + > > return devm_iio_device_register(dev, indio_dev); > > } > > EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345); >
On Sat, 28 Dec 2024 17:11:58 +0100 Lothar Rubusch <l.rubusch@gmail.com> wrote: > On Sat, Dec 28, 2024 at 3:45 PM Jonathan Cameron <jic23@kernel.org> wrote: > > > > On Wed, 25 Dec 2024 18:13:37 +0000 > > Lothar Rubusch <l.rubusch@gmail.com> wrote: > > > > > Add a basic setup for FIFO with configurable watermark. Add a handler > > > for watermark interrupt events and extend the channel for the > > > scan_index needed for the iio channel. The sensor is configurable to use > > > a FIFO_BYPASSED mode or a FIFO_STREAM mode. For the FIFO_STREAM mode now > > > a watermark can be configured, or disabled by setting 0. Further features > > > require a working FIFO setup. > > > > > > Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> > > > --- > > > drivers/iio/accel/adxl345.h | 27 ++- > > > drivers/iio/accel/adxl345_core.c | 305 ++++++++++++++++++++++++++++++- > > > 2 files changed, 321 insertions(+), 11 deletions(-) > > > > > > diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h > > > index 6f39f16d3..bf9e86cff 100644 > > > --- a/drivers/iio/accel/adxl345.h > > > +++ b/drivers/iio/accel/adxl345.h > > > @@ -15,18 +15,32 @@ > > > #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) > > > #define ADXL345_REG_BW_RATE 0x2C > > > #define ADXL345_REG_POWER_CTL 0x2D > > > +#define ADXL345_REG_INT_ENABLE 0x2E > > > +#define ADXL345_REG_INT_MAP 0x2F > > > +#define ADXL345_REG_INT_SOURCE 0x30 > > > +#define ADXL345_REG_INT_SOURCE_MSK 0xFF > > > #define ADXL345_REG_DATA_FORMAT 0x31 > > > -#define ADXL345_REG_DATAX0 0x32 > > > -#define ADXL345_REG_DATAY0 0x34 > > > -#define ADXL345_REG_DATAZ0 0x36 > > > -#define ADXL345_REG_DATA_AXIS(index) \ > > > - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) > > > +#define ADXL345_REG_XYZ_BASE 0x32 > > > +#define ADXL345_REG_DATA_AXIS(index) \ > > > + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) > > > > > > +#define ADXL345_REG_FIFO_CTL 0x38 > > > +#define ADXL345_REG_FIFO_STATUS 0x39 > > > +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F > > > + > > > +#define ADXL345_FIFO_CTL_SAMPLES(x) FIELD_PREP(GENMASK(4, 0), x) > > These need linux/bitfield.h to be included. > > > > Sure, I can do this. > > > However, that got me looking closer at this and it should be done > > differently. > > > > Just define the masks in here and put the FIELD_PREP() inline in the > > c file. Same for the various other FIELD_PREP macros in here. > > > > It may seem convenient to wrap all this up here, but in general > > I'd rather see that these are simple FIELD_PREP() calls where they > > are used inline. > > > > Ok, I understand, the masks stay in the adxl345.h and the FIELD_RPEP() > macros better go to where they are used, i.e. the adxl345_core.c. > > Thank you for reviewing this. I was a bit unsure about using the > FIELD_PREP() / GENMASK() combinations correctly. The code seems to > work for me, though. For future usage of those macros: Generally my > usage of those macros is correct? Yes, the usage is fine otherwise. > > Do I understand correctly, on the long run it would generally be > cleaner, also to migrate the defines to the adxl345_core.c? IMHO, > probably most of the constants in the adxl345.h can be private to > adxl345_core.c, and probably better should. Exactly. Thanks, Jonathan > > Best regards, > L
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 6f39f16d3..bf9e86cff 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -15,18 +15,32 @@ #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) #define ADXL345_REG_BW_RATE 0x2C #define ADXL345_REG_POWER_CTL 0x2D +#define ADXL345_REG_INT_ENABLE 0x2E +#define ADXL345_REG_INT_MAP 0x2F +#define ADXL345_REG_INT_SOURCE 0x30 +#define ADXL345_REG_INT_SOURCE_MSK 0xFF #define ADXL345_REG_DATA_FORMAT 0x31 -#define ADXL345_REG_DATAX0 0x32 -#define ADXL345_REG_DATAY0 0x34 -#define ADXL345_REG_DATAZ0 0x36 -#define ADXL345_REG_DATA_AXIS(index) \ - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) +#define ADXL345_REG_XYZ_BASE 0x32 +#define ADXL345_REG_DATA_AXIS(index) \ + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) +#define ADXL345_REG_FIFO_CTL 0x38 +#define ADXL345_REG_FIFO_STATUS 0x39 +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F + +#define ADXL345_FIFO_CTL_SAMPLES(x) FIELD_PREP(GENMASK(4, 0), x) +/* 0: INT1, 1: INT2 */ +#define ADXL345_FIFO_CTL_TRIGGER(x) FIELD_PREP(BIT(5), x) +#define ADXL345_FIFO_CTL_MODE(x) FIELD_PREP(GENMASK(7, 6), x) + +#define ADXL345_INT_DATA_READY BIT(7) +#define ADXL345_INT_WATERMARK BIT(1) +#define ADXL345_INT_OVERRUN BIT(0) #define ADXL345_BW_RATE GENMASK(3, 0) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_POWER_CTL_STANDBY 0x00 +#define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ #define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ @@ -40,6 +54,7 @@ #define ADXL345_DATA_FORMAT_16G 3 #define ADXL345_DEVID 0xE5 +#define ADXL345_FIFO_SIZE 32 /* * In full-resolution mode, scale factor is maintained at ~4 mg/LSB diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 987a0fe03..738960298 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -15,9 +15,17 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> #include "adxl345.h" +#define ADXL345_FIFO_BYPASS 0 +#define ADXL345_FIFO_FIFO 1 +#define ADXL345_FIFO_STREAM 2 + +#define ADXL345_DIRS 3 + #define ADXL345_INT_NONE 0xff #define ADXL345_INT1 0 #define ADXL345_INT2 1 @@ -28,25 +36,66 @@ struct adxl345_state { bool fifo_delay; /* delay: delay is needed for SPI */ int irq; u8 intio; + u8 int_map; + u8 watermark; + u8 fifo_mode; + __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); }; -#define ADXL345_CHANNEL(index, axis) { \ +#define ADXL345_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ - .address = index, \ + .address = (reg), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ } +enum adxl345_chans { + chan_x, chan_y, chan_z, +}; + static const struct iio_chan_spec adxl345_channels[] = { - ADXL345_CHANNEL(0, X), - ADXL345_CHANNEL(1, Y), - ADXL345_CHANNEL(2, Z), + ADXL345_CHANNEL(0, chan_x, X), + ADXL345_CHANNEL(1, chan_y, Y), + ADXL345_CHANNEL(2, chan_z, Z), }; +static const unsigned long adxl345_scan_masks[] = { + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), + 0 +}; + +static int adxl345_set_interrupts(struct adxl345_state *st) +{ + int ret; + unsigned int int_enable = st->int_map; + unsigned int int_map; + + /* + * Any bits set to 0 in the INT map register send their respective + * interrupts to the INT1 pin, whereas bits set to 1 send their respective + * interrupts to the INT2 pin. The intio shall convert this accordingly. + */ + int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, + st->intio ? st->int_map : ~st->int_map); + + ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map); + if (ret) + return ret; + + return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, int_enable); +} + static int adxl345_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -132,6 +181,24 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value) +{ + struct adxl345_state *st = iio_priv(indio_dev); + unsigned int fifo_mask = 0x1F; + int ret; + + value = min(value, ADXL345_FIFO_SIZE - 1); + + ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value); + if (ret) + return ret; + + st->watermark = value; + st->int_map |= ADXL345_INT_WATERMARK; + + return 0; +} + static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -186,11 +253,217 @@ static const struct attribute_group adxl345_attrs_group = { .attrs = adxl345_attrs, }; +static int adxl345_set_fifo(struct adxl345_state *st) +{ + int ret; + + /* FIFO should only be configured while in standby mode */ + ret = adxl345_set_measure_en(st, false); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, + ADXL345_FIFO_CTL_SAMPLES(st->watermark) | + ADXL345_FIFO_CTL_TRIGGER(st->intio) | + ADXL345_FIFO_CTL_MODE(st->fifo_mode)); + if (ret < 0) + return ret; + + return adxl345_set_measure_en(st, true); +} + +/** + * adxl345_get_samples() - Read number of FIFO entries. + * @st: The initialized state instance of this driver. + * + * The sensor does not support treating any axis individually, or exclude them + * from measuring. + * + * Return: negative error, or value. + */ +static int adxl345_get_samples(struct adxl345_state *st) +{ + unsigned int regval = 0; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, ®val); + if (ret < 0) + return ret; + + return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval); +} + +/** + * adxl345_fifo_transfer() - Read samples number of elements. + * @st: The instance of the state object of this sensor. + * @samples: The number of lines in the FIFO referred to as fifo_entry. + * + * It is recommended that a multiple-byte read of all registers be performed to + * prevent a change in data between reads of sequential registers. That is to + * read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once. + * + * Return: 0 or error value. + */ +static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) +{ + size_t count; + int i, ret = 0; + + /* count is the 3x the fifo_buf element size, hence 6B */ + count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; + for (i = 0; i < samples; i++) { + /* read 3x 2 byte elements from base address into next fifo_buf position */ + ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, + st->fifo_buf + (i * count / 2), count); + if (ret < 0) + return ret; + + /* + * To ensure that the FIFO has completely popped, there must be at least 5 + * us between the end of reading the data registers, signified by the + * transition to register 0x38 from 0x37 or the CS pin going high, and the + * start of new reads of the FIFO or reading the FIFO_STATUS register. For + * SPI operation at 1.5 MHz or lower, the register addressing portion of the + * transmission is sufficient delay to ensure the FIFO has completely + * popped. It is necessary for SPI operation greater than 1.5 MHz to + * de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us + * at 5 MHz operation. + */ + if (st->fifo_delay && samples > 1) + udelay(3); + } + return ret; +} + +/** + * adxl345_fifo_reset() - Empty the FIFO in error condition. + * @st: The instance to the state object of the sensor. + * + * Read all elements of the FIFO. Reading the interrupt source register + * resets the sensor. + */ +static void adxl345_fifo_reset(struct adxl345_state *st) +{ + int regval; + int samples; + + adxl345_set_measure_en(st, false); + + samples = adxl345_get_samples(st); + if (samples > 0) + adxl345_fifo_transfer(st, samples); + + regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); + + adxl345_set_measure_en(st, true); +} + +static int adxl345_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + ret = adxl345_set_interrupts(st); + if (ret < 0) + return ret; + + st->fifo_mode = ADXL345_FIFO_STREAM; + return adxl345_set_fifo(st); +} + +static int adxl345_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + st->fifo_mode = ADXL345_FIFO_BYPASS; + ret = adxl345_set_fifo(st); + if (ret < 0) + return ret; + + st->int_map = 0x00; + return adxl345_set_interrupts(st); +} + +static const struct iio_buffer_setup_ops adxl345_buffer_ops = { + .postenable = adxl345_buffer_postenable, + .predisable = adxl345_buffer_predisable, +}; + +static int adxl345_get_status(struct adxl345_state *st) +{ + int ret; + unsigned int regval; + + ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); + if (ret < 0) + return ret; + + return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval); +} + +static int adxl345_fifo_push(struct iio_dev *indio_dev, + int samples) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int i, ret; + + if (samples <= 0) + return -EINVAL; + + ret = adxl345_fifo_transfer(st, samples); + if (ret) + return ret; + + for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + + return 0; +} + +/** + * adxl345_irq_handler() - Handle irqs of the ADXL345. + * @irq: The irq being handled. + * @p: The struct iio_device pointer for the device. + * + * Return: The interrupt was handled. + */ +static irqreturn_t adxl345_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adxl345_state *st = iio_priv(indio_dev); + int int_stat; + int samples; + + int_stat = adxl345_get_status(st); + if (int_stat <= 0) + return IRQ_NONE; + + if (int_stat & ADXL345_INT_OVERRUN) + goto err; + + if (int_stat & ADXL345_INT_WATERMARK) { + samples = adxl345_get_samples(st); + if (samples < 0) + goto err; + + if (adxl345_fifo_push(indio_dev, samples) < 0) + goto err; + } + return IRQ_HANDLED; + +err: + adxl345_fifo_reset(st); + + return IRQ_HANDLED; +} + static const struct iio_info adxl345_info = { .attrs = &adxl345_attrs_group, .read_raw = adxl345_read_raw, .write_raw = adxl345_write_raw, .write_raw_get_fmt = adxl345_write_raw_get_fmt, + .hwfifo_set_watermark = adxl345_set_watermark, }; /** @@ -221,6 +494,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ADXL345_DATA_FORMAT_JUSTIFY | ADXL345_DATA_FORMAT_FULL_RES | ADXL345_DATA_FORMAT_SELF_TEST); + u8 fifo_ctl; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -239,6 +513,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, 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 (setup) { /* Perform optional initial bus specific configuration */ @@ -289,6 +564,26 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, st->intio = ADXL345_INT_NONE; } + if (st->intio != ADXL345_INT_NONE) { + /* FIFO_STREAM mode is going to be activated later */ + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, st->irq, NULL, + &adxl345_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + } else { + /* FIFO_BYPASS mode */ + fifo_ctl = ADXL345_FIFO_CTL_MODE(ADXL345_FIFO_BYPASS); + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, fifo_ctl); + if (ret < 0) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345);
Add a basic setup for FIFO with configurable watermark. Add a handler for watermark interrupt events and extend the channel for the scan_index needed for the iio channel. The sensor is configurable to use a FIFO_BYPASSED mode or a FIFO_STREAM mode. For the FIFO_STREAM mode now a watermark can be configured, or disabled by setting 0. Further features require a working FIFO setup. Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> --- drivers/iio/accel/adxl345.h | 27 ++- drivers/iio/accel/adxl345_core.c | 305 ++++++++++++++++++++++++++++++- 2 files changed, 321 insertions(+), 11 deletions(-)