On Sun, 19 Aug 2018 18:12:18 +0100 Jonathan Cameron <jic23@xxxxxxxxxx> wrote: > On Fri, 10 Aug 2018 11:46:21 +0300 > Stefan Popa <stefan.popa@xxxxxxxxxx> wrote: > > > This patch adds support for the adxl372 FIFO. In order to accomplish this, > > triggered buffers were used. > > > > The number of FIFO samples which trigger the watermark interrupt can be > > configured by using the buffer watermark. The FIFO format is determined by > > configuring the scan elements for each axis. The FIFO data is pushed to the > > IIO device's buffer. > > > > Signed-off-by: Stefan Popa <stefan.popa@xxxxxxxxxx> > > one minor item I'd missed previously. I'll fix it whilst applying. I was wrong on this, so no need to make that change. > > Applied to the togreg branch of iio.git and pushed out as testing > to see what we've all missed that the autobuilders might find. Dropped for now because of issues in the other email I just sent. I'll hold the rest of the series until those are sorted. Jonathan > > Thanks, > > Jonathan > > > --- > > drivers/iio/accel/adxl372.c | 357 +++++++++++++++++++++++++++++++++++++++++++- > > 1 file changed, 356 insertions(+), 1 deletion(-) > > > > diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c > > index db9ecd2..1e2519a 100644 > > --- a/drivers/iio/accel/adxl372.c > > +++ b/drivers/iio/accel/adxl372.c > > @@ -6,12 +6,19 @@ > > */ > > > > #include <linux/bitops.h> > > +#include <linux/interrupt.h> > > +#include <linux/irq.h> > > #include <linux/module.h> > > #include <linux/regmap.h> > > #include <linux/spi/spi.h> > > > > #include <linux/iio/iio.h> > > #include <linux/iio/sysfs.h> > > +#include <linux/iio/buffer.h> > > +#include <linux/iio/events.h> > > +#include <linux/iio/trigger.h> > > +#include <linux/iio/trigger_consumer.h> > > +#include <linux/iio/triggered_buffer.h> > > > > /* ADXL372 registers definition */ > > #define ADXL372_DEVID 0x00 > > @@ -123,6 +130,9 @@ > > #define ADXL372_INT1_MAP_LOW_MSK BIT(7) > > #define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) > > > > +/* The ADXL372 includes a deep, 512 sample FIFO buffer */ > > +#define ADXL372_FIFO_SIZE 512 > > + > > /* > > * At +/- 200g with 12-bit resolution, scale is computed as: > > * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241 > > @@ -170,6 +180,43 @@ static const unsigned int adxl372_th_reg_high_addr[3] = { > > [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H, > > }; > > > > +enum adxl372_fifo_format { > > + ADXL372_XYZ_FIFO, > > + ADXL372_X_FIFO, > > + ADXL372_Y_FIFO, > > + ADXL372_XY_FIFO, > > + ADXL372_Z_FIFO, > > + ADXL372_XZ_FIFO, > > + ADXL372_YZ_FIFO, > > + ADXL372_XYZ_PEAK_FIFO, > > +}; > > + > > +enum adxl372_fifo_mode { > > + ADXL372_FIFO_BYPASSED, > > + ADXL372_FIFO_STREAMED, > > + ADXL372_FIFO_TRIGGERED, > > + ADXL372_FIFO_OLD_SAVED > > +}; > > + > > +static const int adxl372_samp_freq_tbl[5] = { > > + 400, 800, 1600, 3200, 6400, > > +}; > > + > > +struct adxl372_axis_lookup { > > + unsigned int bits; > > + enum adxl372_fifo_format fifo_format; > > +}; > > + > > +static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { > > + { BIT(0), ADXL372_X_FIFO }, > > + { BIT(1), ADXL372_Y_FIFO }, > > + { BIT(2), ADXL372_Z_FIFO }, > > + { BIT(0) | BIT(1), ADXL372_XY_FIFO }, > > + { BIT(0) | BIT(2), ADXL372_XZ_FIFO }, > > + { BIT(1) | BIT(2), ADXL372_YZ_FIFO }, > > + { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO }, > > +}; > > + > > #define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ > > .type = IIO_ACCEL, \ > > .address = reg, \ > > @@ -177,6 +224,13 @@ static const unsigned int adxl372_th_reg_high_addr[3] = { > > .channel2 = IIO_MOD_##axis, \ > > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ > > + .scan_index = index, \ > > + .scan_type = { \ > > + .sign = 's', \ > > + .realbits = 12, \ > > + .storagebits = 16, \ > > + .shift = 4, \ > > + }, \ > > } > > > > static const struct iio_chan_spec adxl372_channels[] = { > > @@ -188,12 +242,29 @@ static const struct iio_chan_spec adxl372_channels[] = { > > struct adxl372_state { > > struct spi_device *spi; > > struct regmap *regmap; > > + struct iio_trigger *dready_trig; > > + enum adxl372_fifo_mode fifo_mode; > > + enum adxl372_fifo_format fifo_format; > > enum adxl372_op_mode op_mode; > > enum adxl372_act_proc_mode act_proc_mode; > > enum adxl372_odr odr; > > enum adxl372_bandwidth bw; > > u32 act_time_ms; > > u32 inact_time_ms; > > + u8 fifo_set_size; > > + u8 int1_bitmask; > > + u8 int2_bitmask; > > + u16 watermark; > > + __be16 fifo_buf[ADXL372_FIFO_SIZE]; > > +}; > > + > > +static const unsigned long adxl372_channel_masks[] = { > > + BIT(0), BIT(1), BIT(2), > > + BIT(0) | BIT(1), > > + BIT(0) | BIT(2), > > + BIT(1) | BIT(2), > > + BIT(0) | BIT(1) | BIT(2), > > + 0 > > }; > > > > static int adxl372_read_axis(struct adxl372_state *st, u8 addr) > > @@ -359,6 +430,112 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, > > return ret; > > } > > > > +static int adxl372_set_interrupts(struct adxl372_state *st, > > + unsigned char int1_bitmask, > > + unsigned char int2_bitmask) > > +{ > > + int ret; > > + > > + ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask); > > + if (ret < 0) > > + return ret; > > + > > + return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask); > > +} > > + > > +static int adxl372_configure_fifo(struct adxl372_state *st) > > +{ > > + unsigned int fifo_samples, fifo_ctl; > > + int ret; > > + > > + /* FIFO must be configured while in standby mode */ > > + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); > > + if (ret < 0) > > + return ret; > > + > > + fifo_samples = st->watermark & 0xFF; > > + fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) | > > + ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) | > > + ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark); > > + > > + ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples); > > + if (ret < 0) > > + return ret; > > + > > + ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl); > > + if (ret < 0) > > + return ret; > > + > > + return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); > > +} > > + > > +static int adxl372_get_status(struct adxl372_state *st, > > + u8 *status1, u8 *status2, > > + u16 *fifo_entries) > > +{ > > + __be32 buf; > > + u32 val; > > + int ret; > > + > > + /* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */ > > + ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1, > > + &buf, sizeof(buf)); > > + if (ret < 0) > > + return ret; > > + > > + val = be32_to_cpu(buf); > > + > > + *status1 = (val >> 24) & 0x0F; > > + *status2 = (val >> 16) & 0x0F; > > + /* > > + * FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2 > > + * contains the two most significant bits > > + */ > > + *fifo_entries = val & 0x3FF; > > + > > + return ret; > > +} > > + > > +static irqreturn_t adxl372_trigger_handler(int irq, void *p) > > +{ > > + struct iio_poll_func *pf = p; > > + struct iio_dev *indio_dev = pf->indio_dev; > > + struct adxl372_state *st = iio_priv(indio_dev); > > + u8 status1, status2; > > + u16 fifo_entries; > > + int i, ret; > > + > > + ret = adxl372_get_status(st, &status1, &status2, &fifo_entries); > > + if (ret < 0) > > + goto err; > > + > > + if (st->fifo_mode != ADXL372_FIFO_BYPASSED && > > + ADXL372_STATUS_1_FIFO_FULL(status1)) { > > + /* > > + * When reading data from multiple axes from the FIFO, > > + * to ensure that data is not overwritten and stored out > > + * of order at least one sample set must be left in the > > + * FIFO after every read. > > + */ > > + fifo_entries -= st->fifo_set_size; > > + > > + /* Read data from the FIFO */ > > + ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA, > > + st->fifo_buf, > > + fifo_entries * sizeof(u16)); > > + if (ret < 0) > > + goto err; > > + > > + /* Each sample is 2 bytes */ > > + for (i = 0; i < fifo_entries * sizeof(u16); > > + i += st->fifo_set_size * sizeof(u16)) > > + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); > > + } > > +err: > > + iio_trigger_notify_done(indio_dev->trig); > > + return IRQ_HANDLED; > > +} > > + > > static int adxl372_setup(struct adxl372_state *st) > > { > > unsigned int regval; > > @@ -438,7 +615,12 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, > > > > switch (info) { > > case IIO_CHAN_INFO_RAW: > > + ret = iio_device_claim_direct_mode(indio_dev); > > + if (ret) > > + return ret; > > + > > ret = adxl372_read_axis(st, chan->address); > > + iio_device_release_direct_mode(indio_dev); > > if (ret < 0) > > return ret; > > > > @@ -454,16 +636,153 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, > > } > > } > > > > +static ssize_t adxl372_get_fifo_enabled(struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > > + struct adxl372_state *st = iio_priv(indio_dev); > > + > > + return sprintf(buf, "%d\n", st->fifo_mode); > > +} > > + > > +static ssize_t adxl372_get_fifo_watermark(struct device *dev, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > > + struct adxl372_state *st = iio_priv(indio_dev); > > + > > + return sprintf(buf, "%d\n", st->watermark); > > +} > > + > > +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); > > +static IIO_CONST_ATTR(hwfifo_watermark_max, > > + __stringify(ADXL372_FIFO_SIZE)); > > +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, > > + adxl372_get_fifo_watermark, NULL, 0); > > +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, > > + adxl372_get_fifo_enabled, NULL, 0); > > + > > +static const struct attribute *adxl372_fifo_attributes[] = { > > + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, > > + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, > > + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, > > + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, > > + NULL, > > +}; > > + > > +static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val) > > +{ > > + struct adxl372_state *st = iio_priv(indio_dev); > > + > > + if (val > ADXL372_FIFO_SIZE) > > + val = ADXL372_FIFO_SIZE; > > + > > + st->watermark = val; > > + > > + return 0; > > +} > > + > > +static int adxl372_buffer_postenable(struct iio_dev *indio_dev) > > +{ > > + struct adxl372_state *st = iio_priv(indio_dev); > > + unsigned int mask; > > + int i, ret; > > + > > + ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); > > + if (ret < 0) > > + return ret; > > + > > + mask = *indio_dev->active_scan_mask; > > + > > + for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { > > + if (mask == adxl372_axis_lookup_table[i].bits) > > + break; > > + } > > + > > + if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) > > + return -EINVAL; > > + > > + st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; > > + st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, > > + indio_dev->masklength); > > + /* > > + * The 512 FIFO samples can be allotted in several ways, such as: > > + * 170 sample sets of concurrent 3-axis data > > + * 256 sample sets of concurrent 2-axis data (user selectable) > > + * 512 sample sets of single-axis data > > + */ > > + if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) > > + st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); > > + > > + st->fifo_mode = ADXL372_FIFO_STREAMED; > > + > > + ret = adxl372_configure_fifo(st); > > + if (ret < 0) { > > + st->fifo_mode = ADXL372_FIFO_BYPASSED; > > + adxl372_set_interrupts(st, 0, 0); > > + return ret; > > + } > > + > > + return iio_triggered_buffer_postenable(indio_dev); > > +} > > + > > +static int adxl372_buffer_predisable(struct iio_dev *indio_dev) > > +{ > > + struct adxl372_state *st = iio_priv(indio_dev); > > + int ret; > > + > > + ret = iio_triggered_buffer_predisable(indio_dev); > > + if (ret < 0) > > + return ret; > > + > > + adxl372_set_interrupts(st, 0, 0); > > + st->fifo_mode = ADXL372_FIFO_BYPASSED; > > + adxl372_configure_fifo(st); > > + > > + return 0; > > +} > > + > > +static const struct iio_buffer_setup_ops adxl372_buffer_ops = { > > + .postenable = adxl372_buffer_postenable, > > + .predisable = adxl372_buffer_predisable, > > +}; > > + > > +static int adxl372_dready_trig_set_state(struct iio_trigger *trig, > > + bool state) > > +{ > > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > > + struct adxl372_state *st = iio_priv(indio_dev); > > + unsigned long int mask = 0; > > + > > + if (state) > > + mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; > > + > > + return adxl372_set_interrupts(st, mask, 0); > > +} > > + > > +static const struct iio_trigger_ops adxl372_trigger_ops = { > > + .set_trigger_state = adxl372_dready_trig_set_state, > > +}; > > + > > static const struct iio_info adxl372_info = { > > .read_raw = adxl372_read_raw, > > .debugfs_reg_access = &adxl372_reg_access, > > + .hwfifo_set_watermark = adxl372_set_watermark, > > }; > > > > +static bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) > > +{ > > + return (reg == ADXL372_FIFO_DATA); > > +} > > + > > static const struct regmap_config adxl372_spi_regmap_config = { > > .reg_bits = 7, > > .pad_bits = 1, > > .val_bits = 8, > > .read_flag_mask = BIT(0), > > + .readable_noinc_reg = adxl372_readable_noinc_reg, > > }; > > > > static int adxl372_probe(struct spi_device *spi) > > @@ -490,10 +809,11 @@ static int adxl372_probe(struct spi_device *spi) > > > > indio_dev->channels = adxl372_channels; > > indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); > > + indio_dev->available_scan_masks = adxl372_channel_masks; > > indio_dev->dev.parent = &spi->dev; > > indio_dev->name = spi_get_device_id(spi)->name; > > indio_dev->info = &adxl372_info; > > - indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; > > This might be wrong if we don't have an interrupt. > I'll move it when applying to be under the if (st->spi->irq) below. > > > > > > > ret = adxl372_setup(st); > > if (ret < 0) { > > @@ -501,6 +821,41 @@ static int adxl372_probe(struct spi_device *spi) > > return ret; > > } > > > > + ret = devm_iio_triggered_buffer_setup(&st->spi->dev, > > + indio_dev, NULL, > > + adxl372_trigger_handler, > > + &adxl372_buffer_ops); > > + if (ret < 0) > > + return ret; > > + > > + iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); > > + > > + if (st->spi->irq) { > > + st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, > > + "%s-dev%d", > > + indio_dev->name, > > + indio_dev->id); > > + if (st->dready_trig == NULL) > > + return -ENOMEM; > > + > > + st->dready_trig->ops = &adxl372_trigger_ops; > > + st->dready_trig->dev.parent = &st->spi->dev; > > + iio_trigger_set_drvdata(st->dready_trig, indio_dev); > > + ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); > > + if (ret < 0) > > + return ret; > > + > > + indio_dev->trig = iio_trigger_get(st->dready_trig); > > + > > + ret = devm_request_threaded_irq(&st->spi->dev, st->spi->irq, > > + iio_trigger_generic_data_rdy_poll, > > + NULL, > > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > > + indio_dev->name, st->dready_trig); > > + if (ret < 0) > > + return ret; > > + } > > + > > return devm_iio_device_register(&st->spi->dev, indio_dev); > > } > > >