Add the handling of activity events, also add sysfs entries to
configure threshold values to trigger the event. Allow to push the
event over to the iio channel.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl345_core.c | 158 ++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 62d75d28b6fc..94c3ad818ba5 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -121,6 +121,8 @@
#define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0)
#define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3)
+#define ADXL345_REG_ACT_AXIS_MSK GENMASK(6, 4)
+#define ADXL345_REG_ACT_ACDC_MSK BIT(7)
enum adxl345_axis {
ADXL345_Z_EN = BIT(0),
@@ -163,6 +165,10 @@ struct adxl345_state {
u8 watermark;
u8 fifo_mode;
+ u32 act_axis_ctrl;
+ bool act_ac;
+ u8 act_value;
+
u32 tap_axis_ctrl;
u8 tap_threshold;
u32 tap_duration_us;
@@ -177,6 +183,11 @@ struct adxl345_state {
};
static struct iio_event_spec adxl345_events[] = {
+ {
+ /* activity */
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ },
{
/* single tap */
.type = IIO_EV_TYPE_GESTURE,
@@ -276,6 +287,117 @@ static inline int adxl345_write_interrupts(struct adxl345_state *st)
return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map);
}
+/* act/inact */
+
+static int adxl345_write_act_axis(struct adxl345_state *st, bool en)
+{
+ int ret;
+
+ /*
+ * A setting of 0 selects dc-coupled operation, and a setting of 1
+ * enables ac-coupled operation. In dc-coupled operation, the current
+ * acceleration magnitude is compared directly with THRESH_ACT and
+ * THRESH_INACT to determine whether activity or inactivity is
+ * detected.
+ *
+ * In ac-coupled operation for activity detection, the acceleration
+ * value at the start of activity detection is taken as a reference
+ * value. New samples of acceleration are then compared to this
+ * reference value, and if the magnitude of the difference exceeds the
+ * THRESH_ACT value, the device triggers an activity interrupt.
+ *
+ * Similarly, in ac-coupled operation for inactivity detection, a
+ * reference value is used for comparison and is updated whenever the
+ * device exceeds the inactivity threshold. After the reference value
+ * is selected, the device compares the magnitude of the difference
+ * between the reference value and the current acceleration with
+ * THRESH_INACT. If the difference is less than the value in
+ * THRESH_INACT for the time in TIME_INACT, the device is considered
+ * inactive and the inactivity interrupt is triggered.
+ */
+ ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
+ ADXL345_REG_ACT_ACDC_MSK, st->act_ac);
+ if (ret)
+ return ret;
+
+ /*
+ * The ADXL345 allows for individually enabling/disabling axis for
+ * activity and inactivity detection, respectively. Here both axis are
+ * kept in sync, i.e. an axis will be generally enabled or disabled for
+ * both equally, activity and inactivity detection.
+ */
+ st->act_axis_ctrl = en
+ ? st->act_axis_ctrl | ADXL345_REG_ACT_AXIS_MSK
+ : st->act_axis_ctrl & ~ADXL345_REG_ACT_AXIS_MSK;
+
+ ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
+ ADXL345_REG_ACT_AXIS_MSK,
+ st->act_axis_ctrl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int adxl345_set_act_int(struct adxl345_state *st)
+{
+ bool args_valid;
+ bool axis_en;
+
+ axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
+ args_valid = axis_en && st->act_value > 0;
+ adxl345_intmap_switch_bit(st, args_valid, ADXL345_INT_ACTIVITY);
+
+ return adxl345_write_interrupts(st);
+}
+
+static int _adxl345_is_act_en(struct adxl345_state *st, bool *en)
+{
+ int ret;
+ unsigned int regval;
+
+ ret = adxl345_read_interrupts(st, ®val);
+ if (ret)
+ return ret;
+
+ *en = FIELD_GET(ADXL345_INT_ACTIVITY, regval) > 0;
+
+ return 0;
+}
+
+static int _adxl345_set_act_en(struct adxl345_state *st, bool en)
+{
+ int ret;
+
+ ret = adxl345_write_act_axis(st, en);
+ if (ret)
+ return ret;
+
+ return adxl345_set_act_int(st);
+}
+
+static int adxl345_is_act_en(struct adxl345_state *st, bool *en)
+{
+ return _adxl345_is_act_en(st, en);
+}
+
+static int adxl345_set_act_en(struct adxl345_state *st, bool en)
+{
+ return _adxl345_set_act_en(st, en);
+}
+
+static int _adxl345_set_act_value(struct adxl345_state *st, u8 val)
+{
+ st->act_value = val;
+
+ return regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, val);
+}
+
+static int adxl345_set_act_value(struct adxl345_state *st, u8 val)
+{
+ return _adxl345_set_act_value(st, val);
+}
+
/* tap */
static int adxl345_write_tap_axis(struct adxl345_state *st,
@@ -904,6 +1026,7 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
} \
static IIO_DEVICE_ATTR_RW(in_accel_##A##_##C##_##E, 0)
+ADXL345_generate_iio_dev_attr_INT(activity, act, value);
ADXL345_generate_iio_dev_attr_INT(freefall, ff, value);
ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_singletap, tap, duration, MICRO, us);
@@ -911,10 +1034,13 @@ ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, window, MICRO,
ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, latent, MICRO, us);
ADXL345_generate_iio_dev_attr_FRACTIONAL(freefall, ff, time, MILLI, ms);
+ADXL345_generate_iio_dev_attr_EN(activity, act);
ADXL345_generate_iio_dev_attr_EN(freefall, ff);
ADXL345_generate_iio_dev_attr_EN(gesture_doubletap, suppressed);
static struct attribute *adxl345_event_attrs[] = {
+ &iio_dev_attr_in_accel_activity_act_en.dev_attr.attr,
+ &iio_dev_attr_in_accel_activity_act_value.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_ff_en.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_ff_value.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_time_ms.dev_attr.attr,
@@ -1087,20 +1213,25 @@ static int adxl345_get_status(struct adxl345_state *st, unsigned int *int_stat,
{
unsigned int regval;
bool check_tap_stat;
+ bool check_act_stat;
*act_tap_dir = IIO_NO_MOD;
check_tap_stat = FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, st->tap_axis_ctrl) > 0;
+ check_act_stat = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
- if (check_tap_stat) {
+ if (check_tap_stat || check_act_stat) {
/* ACT_TAP_STATUS should be read before clearing the interrupt */
if (regmap_read(st->regmap, ADXL345_REG_ACT_TAP_STATUS, ®val))
return -EINVAL;
- if (FIELD_GET(ADXL345_Z_EN, regval) > 0)
+ if ((FIELD_GET(ADXL345_Z_EN, regval >> 4)
+ | FIELD_GET(ADXL345_Z_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_Z;
- else if (FIELD_GET(ADXL345_Y_EN, regval) > 0)
+ else if ((FIELD_GET(ADXL345_Y_EN, regval >> 4)
+ | FIELD_GET(ADXL345_Y_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_Y;
- else if (FIELD_GET(ADXL345_X_EN, regval) > 0)
+ else if ((FIELD_GET(ADXL345_X_EN, regval >> 4)
+ | FIELD_GET(ADXL345_X_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_X;
}
@@ -1154,6 +1285,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat,
return ret;
}
+ if (FIELD_GET(ADXL345_INT_ACTIVITY, int_stat)) {
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ act_tap_dir,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ ts);
+ if (ret)
+ return ret;
+ }
+
if (FIELD_GET(ADXL345_INT_FREE_FALL, int_stat)) {
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
@@ -1264,6 +1406,13 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
return -ENODEV;
st->fifo_delay = fifo_delay_default;
+ /*
+ * If the feature is enabled, scan all axis for activity and or
+ * inactivity, and set activity and inactivity to the same ac / dc
+ * setup.
+ */
+ st->act_axis_ctrl = ADXL345_REG_ACT_AXIS_MSK;
+ st->act_ac = 0;
st->int_map = 0x00; /* reset interrupts */
/* Init with reasonable values */
@@ -1272,6 +1421,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
st->tap_window_us = 20; /* 20 [0x14] -> .025 */
st->tap_latent_us = 20; /* 20 [0x14] -> .025 */
+ st->act_value = 6; /* 6 [0x06] */
st->ff_value = 8; /* 8 [0x08] */
st->ff_time_ms = 32; /* 32 [0x20] -> 0.16 */
--
2.39.5
On Tue, 28 Jan 2025 12:00:59 +0000
Lothar Rubusch <l.rubusch@gmail.com> wrote:
> Add the handling of activity events, also add sysfs entries to
> configure threshold values to trigger the event. Allow to push the
> event over to the iio channel.
>
> Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
I was going to guess these were rate of change detectors or ones
at least slightly compensating for g, but superficially
looks like a straight forward rising mag threshold. Not seeing need
for new ABI.
We've had those interfaces with accelerometers for a long time.
Key is to map the pretty names in the datasheet into what is actually
being detected. That allows for a lot better generalization across manufacturers.
> ---
> drivers/iio/accel/adxl345_core.c | 158 ++++++++++++++++++++++++++++++-
> 1 file changed, 154 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
> index 62d75d28b6fc..94c3ad818ba5 100644
> --- a/drivers/iio/accel/adxl345_core.c
> +++ b/drivers/iio/accel/adxl345_core.c
> @@ -121,6 +121,8 @@
>
> #define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0)
> #define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3)
> +#define ADXL345_REG_ACT_AXIS_MSK GENMASK(6, 4)
> +#define ADXL345_REG_ACT_ACDC_MSK BIT(7)
>
> enum adxl345_axis {
> ADXL345_Z_EN = BIT(0),
> @@ -163,6 +165,10 @@ struct adxl345_state {
> u8 watermark;
> u8 fifo_mode;
>
> + u32 act_axis_ctrl;
> + bool act_ac;
> + u8 act_value;
> +
> u32 tap_axis_ctrl;
> u8 tap_threshold;
> u32 tap_duration_us;
> @@ -177,6 +183,11 @@ struct adxl345_state {
> };
>
> static struct iio_event_spec adxl345_events[] = {
> + {
> + /* activity */
> + .type = IIO_EV_TYPE_THRESH,
> + .dir = IIO_EV_DIR_RISING,
> + },
> {
> /* single tap */
> .type = IIO_EV_TYPE_GESTURE,
> @@ -276,6 +287,117 @@ static inline int adxl345_write_interrupts(struct adxl345_state *st)
> return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map);
> }
>
> +/* act/inact */
> +
> +static int adxl345_write_act_axis(struct adxl345_state *st, bool en)
> +{
> + int ret;
> +
> + /*
> + * A setting of 0 selects dc-coupled operation, and a setting of 1
> + * enables ac-coupled operation. In dc-coupled operation, the current
> + * acceleration magnitude is compared directly with THRESH_ACT and
> + * THRESH_INACT to determine whether activity or inactivity is
> + * detected.
> + *
> + * In ac-coupled operation for activity detection, the acceleration
> + * value at the start of activity detection is taken as a reference
> + * value. New samples of acceleration are then compared to this
> + * reference value, and if the magnitude of the difference exceeds the
> + * THRESH_ACT value, the device triggers an activity interrupt.
> + *
> + * Similarly, in ac-coupled operation for inactivity detection, a
> + * reference value is used for comparison and is updated whenever the
> + * device exceeds the inactivity threshold. After the reference value
> + * is selected, the device compares the magnitude of the difference
> + * between the reference value and the current acceleration with
> + * THRESH_INACT. If the difference is less than the value in
> + * THRESH_INACT for the time in TIME_INACT, the device is considered
> + * inactive and the inactivity interrupt is triggered.
> + */
> + ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
> + ADXL345_REG_ACT_ACDC_MSK, st->act_ac);
> + if (ret)
> + return ret;
> +
> + /*
> + * The ADXL345 allows for individually enabling/disabling axis for
> + * activity and inactivity detection, respectively. Here both axis are
> + * kept in sync, i.e. an axis will be generally enabled or disabled for
> + * both equally, activity and inactivity detection.
> + */
> + st->act_axis_ctrl = en
> + ? st->act_axis_ctrl | ADXL345_REG_ACT_AXIS_MSK
> + : st->act_axis_ctrl & ~ADXL345_REG_ACT_AXIS_MSK;
> +
> + ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
> + ADXL345_REG_ACT_AXIS_MSK,
> + st->act_axis_ctrl);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int adxl345_set_act_int(struct adxl345_state *st)
> +{
> + bool args_valid;
> + bool axis_en;
> +
> + axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
> + args_valid = axis_en && st->act_value > 0;
> + adxl345_intmap_switch_bit(st, args_valid, ADXL345_INT_ACTIVITY);
> +
> + return adxl345_write_interrupts(st);
> +}
> +
> +static int _adxl345_is_act_en(struct adxl345_state *st, bool *en)
> +{
> + int ret;
> + unsigned int regval;
> +
> + ret = adxl345_read_interrupts(st, ®val);
> + if (ret)
> + return ret;
> +
> + *en = FIELD_GET(ADXL345_INT_ACTIVITY, regval) > 0;
> +
> + return 0;
> +}
> +
> +static int _adxl345_set_act_en(struct adxl345_state *st, bool en)
> +{
> + int ret;
> +
> + ret = adxl345_write_act_axis(st, en);
> + if (ret)
> + return ret;
> +
> + return adxl345_set_act_int(st);
> +}
> +
> +static int adxl345_is_act_en(struct adxl345_state *st, bool *en)
> +{
> + return _adxl345_is_act_en(st, en);
> +}
> +
> +static int adxl345_set_act_en(struct adxl345_state *st, bool en)
> +{
> + return _adxl345_set_act_en(st, en);
> +}
> +
> +static int _adxl345_set_act_value(struct adxl345_state *st, u8 val)
> +{
> + st->act_value = val;
> +
> + return regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, val);
> +}
> +
> +static int adxl345_set_act_value(struct adxl345_state *st, u8 val)
> +{
> + return _adxl345_set_act_value(st, val);
> +}
On Sat, Feb 1, 2025 at 6:27 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Tue, 28 Jan 2025 12:00:59 +0000
> Lothar Rubusch <l.rubusch@gmail.com> wrote:
>
> > Add the handling of activity events, also add sysfs entries to
> > configure threshold values to trigger the event. Allow to push the
> > event over to the iio channel.
> >
> > Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
>
> I was going to guess these were rate of change detectors or ones
> at least slightly compensating for g, but superficially
> looks like a straight forward rising mag threshold. Not seeing need
> for new ABI.
>
> We've had those interfaces with accelerometers for a long time.
> Key is to map the pretty names in the datasheet into what is actually
> being detected. That allows for a lot better generalization across manufacturers.
>
I understand. The adxl345 should actually be nothing fancy. I prepare
something more IIO-like event handling for the next version of the
patches. Thank you for the feedback.
> > ---
> > drivers/iio/accel/adxl345_core.c | 158 ++++++++++++++++++++++++++++++-
> > 1 file changed, 154 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
> > index 62d75d28b6fc..94c3ad818ba5 100644
> > --- a/drivers/iio/accel/adxl345_core.c
> > +++ b/drivers/iio/accel/adxl345_core.c
> > @@ -121,6 +121,8 @@
> >
> > #define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0)
> > #define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3)
> > +#define ADXL345_REG_ACT_AXIS_MSK GENMASK(6, 4)
> > +#define ADXL345_REG_ACT_ACDC_MSK BIT(7)
> >
> > enum adxl345_axis {
> > ADXL345_Z_EN = BIT(0),
> > @@ -163,6 +165,10 @@ struct adxl345_state {
> > u8 watermark;
> > u8 fifo_mode;
> >
> > + u32 act_axis_ctrl;
> > + bool act_ac;
> > + u8 act_value;
> > +
> > u32 tap_axis_ctrl;
> > u8 tap_threshold;
> > u32 tap_duration_us;
> > @@ -177,6 +183,11 @@ struct adxl345_state {
> > };
> >
> > static struct iio_event_spec adxl345_events[] = {
> > + {
> > + /* activity */
> > + .type = IIO_EV_TYPE_THRESH,
> > + .dir = IIO_EV_DIR_RISING,
> > + },
> > {
> > /* single tap */
> > .type = IIO_EV_TYPE_GESTURE,
> > @@ -276,6 +287,117 @@ static inline int adxl345_write_interrupts(struct adxl345_state *st)
> > return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map);
> > }
> >
> > +/* act/inact */
> > +
> > +static int adxl345_write_act_axis(struct adxl345_state *st, bool en)
> > +{
> > + int ret;
> > +
> > + /*
> > + * A setting of 0 selects dc-coupled operation, and a setting of 1
> > + * enables ac-coupled operation. In dc-coupled operation, the current
> > + * acceleration magnitude is compared directly with THRESH_ACT and
> > + * THRESH_INACT to determine whether activity or inactivity is
> > + * detected.
> > + *
> > + * In ac-coupled operation for activity detection, the acceleration
> > + * value at the start of activity detection is taken as a reference
> > + * value. New samples of acceleration are then compared to this
> > + * reference value, and if the magnitude of the difference exceeds the
> > + * THRESH_ACT value, the device triggers an activity interrupt.
> > + *
> > + * Similarly, in ac-coupled operation for inactivity detection, a
> > + * reference value is used for comparison and is updated whenever the
> > + * device exceeds the inactivity threshold. After the reference value
> > + * is selected, the device compares the magnitude of the difference
> > + * between the reference value and the current acceleration with
> > + * THRESH_INACT. If the difference is less than the value in
> > + * THRESH_INACT for the time in TIME_INACT, the device is considered
> > + * inactive and the inactivity interrupt is triggered.
> > + */
> > + ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
> > + ADXL345_REG_ACT_ACDC_MSK, st->act_ac);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * The ADXL345 allows for individually enabling/disabling axis for
> > + * activity and inactivity detection, respectively. Here both axis are
> > + * kept in sync, i.e. an axis will be generally enabled or disabled for
> > + * both equally, activity and inactivity detection.
> > + */
> > + st->act_axis_ctrl = en
> > + ? st->act_axis_ctrl | ADXL345_REG_ACT_AXIS_MSK
> > + : st->act_axis_ctrl & ~ADXL345_REG_ACT_AXIS_MSK;
> > +
> > + ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
> > + ADXL345_REG_ACT_AXIS_MSK,
> > + st->act_axis_ctrl);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +static int adxl345_set_act_int(struct adxl345_state *st)
> > +{
> > + bool args_valid;
> > + bool axis_en;
> > +
> > + axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
> > + args_valid = axis_en && st->act_value > 0;
> > + adxl345_intmap_switch_bit(st, args_valid, ADXL345_INT_ACTIVITY);
> > +
> > + return adxl345_write_interrupts(st);
> > +}
> > +
> > +static int _adxl345_is_act_en(struct adxl345_state *st, bool *en)
> > +{
> > + int ret;
> > + unsigned int regval;
> > +
> > + ret = adxl345_read_interrupts(st, ®val);
> > + if (ret)
> > + return ret;
> > +
> > + *en = FIELD_GET(ADXL345_INT_ACTIVITY, regval) > 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int _adxl345_set_act_en(struct adxl345_state *st, bool en)
> > +{
> > + int ret;
> > +
> > + ret = adxl345_write_act_axis(st, en);
> > + if (ret)
> > + return ret;
> > +
> > + return adxl345_set_act_int(st);
> > +}
> > +
> > +static int adxl345_is_act_en(struct adxl345_state *st, bool *en)
> > +{
> > + return _adxl345_is_act_en(st, en);
> > +}
> > +
> > +static int adxl345_set_act_en(struct adxl345_state *st, bool en)
> > +{
> > + return _adxl345_set_act_en(st, en);
> > +}
> > +
> > +static int _adxl345_set_act_value(struct adxl345_state *st, u8 val)
> > +{
> > + st->act_value = val;
> > +
> > + return regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, val);
> > +}
> > +
> > +static int adxl345_set_act_value(struct adxl345_state *st, u8 val)
> > +{
> > + return _adxl345_set_act_value(st, val);
> > +}
>
© 2016 - 2025 Red Hat, Inc.