Message ID | 20250414184245.100280-6-l.rubusch@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | iio: accel: adxl345: add interrupt based sensor events | expand |
On Mon, 14 Apr 2025 18:42:39 +0000 Lothar Rubusch <l.rubusch@gmail.com> wrote: > Add the freefall detection of the sensor together with a threshold and > time parameter. A freefall event is detected if the measuring signal > falls below the threshold. > > Introduce a freefall threshold stored in regmap cache, and a freefall > time, having the scaled time value stored as a member variable in the > state instance. > Reading this I wondered whether we had the event code consistent for freefall detectors... Or indeed inactivity ones (which are kind of similarish) :( We don't it seems. The issue is that freefall is actually that all channels are simultaneously under the the magnitude threshold, not one of them. So it should I think be X_AND_Y_AND_Z not X_OR_Y_OR_Z This is as opposed to activity detectors which tend to be any axis shows activity and X_OR_Y_OR_Z applies. Anyhow upshot is I think I lead you astray on this and we should make this one IIO_MOD_X_AND_Y_AND_Z A few other things inline. Unfortunately we don't deal with these events that often so I forget what we did before :( > Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> > --- > drivers/iio/accel/adxl345_core.c | 125 +++++++++++++++++++++++++++++++ > 1 file changed, 125 insertions(+) > > diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c > index c464c87033fb..ae02826e552b 100644 > --- a/drivers/iio/accel/adxl345_core.c > +++ b/drivers/iio/accel/adxl345_core.c > @@ -75,6 +75,7 @@ struct adxl345_state { > u32 tap_duration_us; > u32 tap_latent_us; > u32 tap_window_us; > + u32 ff_time_ms; > > __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); > }; > @@ -96,6 +97,14 @@ static struct iio_event_spec adxl345_events[] = { > BIT(IIO_EV_INFO_RESET_TIMEOUT) | > BIT(IIO_EV_INFO_TAP2_MIN_DELAY), > }, > + { > + /* free fall */ > + .type = IIO_EV_TYPE_MAG, > + .dir = IIO_EV_DIR_FALLING, > + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | > + BIT(IIO_EV_INFO_VALUE) | > + BIT(IIO_EV_INFO_PERIOD), > + }, This is creating separate per axis enables, values and period. Does that make sense? If not you need to spin a kind of virtual channel (with mod X_AND_Y_AND_Z) and add the events to it. See how the sca3000 does it for example. > }; > > #define ADXL345_CHANNEL(index, reg, axis) { \ > @@ -383,6 +392,63 @@ static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int, > return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us); > } > > +/* freefall */ > + > +static int adxl345_is_ff_en(struct adxl345_state *st, bool *en) > +{ > + int ret; > + unsigned int regval; > + > + ret = regmap_read(st->regmap, ADXL345_REG_INT_ENABLE, ®val); > + if (ret) > + return ret; > + > + *en = FIELD_GET(ADXL345_INT_FREE_FALL, regval) > 0; > + > + return 0; > +} > + > +static int adxl345_set_ff_en(struct adxl345_state *st, bool cmd_en) > +{ > + unsigned int regval, ff_threshold; > + bool en; > + int ret; > + > + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_FF, &ff_threshold); > + if (ret) > + return ret; > + > + en = cmd_en && ff_threshold > 0 && st->ff_time_ms > 0; > + > + regval = en ? ADXL345_INT_FREE_FALL : 0x00; > + > + return regmap_update_bits(st->regmap, ADXL345_REG_INT_ENABLE, > + ADXL345_INT_FREE_FALL, regval); > +} > + > +static int adxl345_set_ff_time(struct adxl345_state *st, u32 val_int, > + u32 val_fract_us) > +{ > + unsigned int regval; > + int val_ms; > + > + /* > + * max value is 255 * 5000 us = 1.275000 seconds > + * > + * Note: the scaling is similar to the scaling in the ADXL380 > + */ > + if (1000000 * val_int + val_fract_us > 1275000) > + return -EINVAL; > + > + val_ms = val_int * 1000 + DIV_ROUND_UP(val_fract_us, 1000); > + st->ff_time_ms = val_ms; > + > + regval = DIV_ROUND_CLOSEST(val_ms, 5); > + > + /* Values between 100ms and 350ms (0x14 to 0x46) are recommended. */ > + return regmap_write(st->regmap, ADXL345_REG_TIME_FF, min(regval, 0xff)); > +} > + > static int adxl345_read_raw(struct iio_dev *indio_dev, > struct iio_chan_spec const *chan, > int *val, int *val2, long mask) > @@ -495,6 +561,11 @@ static int adxl345_read_event_config(struct iio_dev *indio_dev, > default: > return -EINVAL; > } > + case IIO_EV_TYPE_MAG: > + ret = adxl345_is_ff_en(st, &int_en); > + if (ret) > + return ret; > + return int_en; > default: > return -EINVAL; > } > @@ -518,6 +589,8 @@ static int adxl345_write_event_config(struct iio_dev *indio_dev, > default: > return -EINVAL; > } > + case IIO_EV_TYPE_MAG: > + return adxl345_set_ff_en(st, state); > default: > return -EINVAL; > } > @@ -532,6 +605,7 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, > { > struct adxl345_state *st = iio_priv(indio_dev); > unsigned int tap_threshold; > + unsigned int ff_threshold; > int ret; > > switch (type) { > @@ -565,6 +639,22 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, > default: > return -EINVAL; > } > + case IIO_EV_TYPE_MAG: > + switch (info) { > + case IIO_EV_INFO_VALUE: > + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_FF, > + &ff_threshold); > + if (ret) > + return ret; > + *val = ff_threshold; > + return IIO_VAL_INT; > + case IIO_EV_INFO_PERIOD: > + *val = st->ff_time_ms; > + *val2 = 1000; > + return IIO_VAL_FRACTIONAL; > + default: > + return -EINVAL; > + } > default: > return -EINVAL; > } > @@ -612,6 +702,22 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev, > return -EINVAL; > } > break; > + case IIO_EV_TYPE_MAG: > + switch (info) { > + case IIO_EV_INFO_VALUE: > + ret = regmap_write(st->regmap, ADXL345_REG_THRESH_FF, val); > + if (ret) > + return ret; > + break; > + case IIO_EV_INFO_PERIOD: > + ret = adxl345_set_ff_time(st, val, val2); > + if (ret) > + return ret; > + break; > + default: > + return -EINVAL; > + } > + break; > default: > return -EINVAL; > } > @@ -865,6 +971,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat, > return ret; > } > > + if (FIELD_GET(ADXL345_INT_FREE_FALL, int_stat)) { > + ret = iio_push_event(indio_dev, > + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, > + IIO_MOD_X_OR_Y_OR_Z, This is the event that got me thinking about whether we were doing this right.. > + IIO_EV_TYPE_MAG, > + IIO_EV_DIR_FALLING), > + ts); > + if (ret) > + return ret; > + } > +
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index c464c87033fb..ae02826e552b 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -75,6 +75,7 @@ struct adxl345_state { u32 tap_duration_us; u32 tap_latent_us; u32 tap_window_us; + u32 ff_time_ms; __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); }; @@ -96,6 +97,14 @@ static struct iio_event_spec adxl345_events[] = { BIT(IIO_EV_INFO_RESET_TIMEOUT) | BIT(IIO_EV_INFO_TAP2_MIN_DELAY), }, + { + /* free fall */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, }; #define ADXL345_CHANNEL(index, reg, axis) { \ @@ -383,6 +392,63 @@ static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int, return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us); } +/* freefall */ + +static int adxl345_is_ff_en(struct adxl345_state *st, bool *en) +{ + int ret; + unsigned int regval; + + ret = regmap_read(st->regmap, ADXL345_REG_INT_ENABLE, ®val); + if (ret) + return ret; + + *en = FIELD_GET(ADXL345_INT_FREE_FALL, regval) > 0; + + return 0; +} + +static int adxl345_set_ff_en(struct adxl345_state *st, bool cmd_en) +{ + unsigned int regval, ff_threshold; + bool en; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_FF, &ff_threshold); + if (ret) + return ret; + + en = cmd_en && ff_threshold > 0 && st->ff_time_ms > 0; + + regval = en ? ADXL345_INT_FREE_FALL : 0x00; + + return regmap_update_bits(st->regmap, ADXL345_REG_INT_ENABLE, + ADXL345_INT_FREE_FALL, regval); +} + +static int adxl345_set_ff_time(struct adxl345_state *st, u32 val_int, + u32 val_fract_us) +{ + unsigned int regval; + int val_ms; + + /* + * max value is 255 * 5000 us = 1.275000 seconds + * + * Note: the scaling is similar to the scaling in the ADXL380 + */ + if (1000000 * val_int + val_fract_us > 1275000) + return -EINVAL; + + val_ms = val_int * 1000 + DIV_ROUND_UP(val_fract_us, 1000); + st->ff_time_ms = val_ms; + + regval = DIV_ROUND_CLOSEST(val_ms, 5); + + /* Values between 100ms and 350ms (0x14 to 0x46) are recommended. */ + return regmap_write(st->regmap, ADXL345_REG_TIME_FF, min(regval, 0xff)); +} + static int adxl345_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -495,6 +561,11 @@ static int adxl345_read_event_config(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_EV_TYPE_MAG: + ret = adxl345_is_ff_en(st, &int_en); + if (ret) + return ret; + return int_en; default: return -EINVAL; } @@ -518,6 +589,8 @@ static int adxl345_write_event_config(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_EV_TYPE_MAG: + return adxl345_set_ff_en(st, state); default: return -EINVAL; } @@ -532,6 +605,7 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, { struct adxl345_state *st = iio_priv(indio_dev); unsigned int tap_threshold; + unsigned int ff_threshold; int ret; switch (type) { @@ -565,6 +639,22 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_EV_TYPE_MAG: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_FF, + &ff_threshold); + if (ret) + return ret; + *val = ff_threshold; + return IIO_VAL_INT; + case IIO_EV_INFO_PERIOD: + *val = st->ff_time_ms; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -612,6 +702,22 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev, return -EINVAL; } break; + case IIO_EV_TYPE_MAG: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_write(st->regmap, ADXL345_REG_THRESH_FF, val); + if (ret) + return ret; + break; + case IIO_EV_INFO_PERIOD: + ret = adxl345_set_ff_time(st, val, val2); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } @@ -865,6 +971,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat, return ret; } + if (FIELD_GET(ADXL345_INT_FREE_FALL, int_stat)) { + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_FALLING), + ts); + if (ret) + return ret; + } + if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) { samples = adxl345_get_samples(st); if (samples < 0) @@ -973,6 +1090,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ADXL345_DATA_FORMAT_FULL_RES | ADXL345_DATA_FORMAT_SELF_TEST); unsigned int tap_threshold; + unsigned int ff_threshold; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -992,6 +1110,9 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, st->tap_window_us = 64; /* 64 [0x40] -> .080 */ st->tap_latent_us = 16; /* 16 [0x10] -> .020 */ + ff_threshold = 8; /* 8 [0x08] */ + st->ff_time_ms = 32; /* 32 [0x20] -> 0.16 */ + indio_dev->name = st->info->name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1068,6 +1189,10 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; + ret = regmap_write(st->regmap, ADXL345_REG_THRESH_FF, ff_threshold); + if (ret) + return ret; + /* FIFO_STREAM mode is going to be activated later */ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); if (ret)
Add the freefall detection of the sensor together with a threshold and time parameter. A freefall event is detected if the measuring signal falls below the threshold. Introduce a freefall threshold stored in regmap cache, and a freefall time, having the scaled time value stored as a member variable in the state instance. Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> --- drivers/iio/accel/adxl345_core.c | 125 +++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+)