Extend the interrupt handler to process interrupts as inactivity events.
Add functions to set threshold and period registers for inactivity. Add
functions to enable / disable inactivity. Extend the fake iio channel to
deal with inactivity events on x, y and z combined with AND.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 2 +
drivers/iio/accel/adxl313_core.c | 149 ++++++++++++++++++++++++-------
2 files changed, 121 insertions(+), 30 deletions(-)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 8c68cff7569c..374e4bfe6e05 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -18,6 +18,8 @@
#define ADXL313_REG_SOFT_RESET 0x18
#define ADXL313_REG_OFS_AXIS(index) (0x1E + (index))
#define ADXL313_REG_THRESH_ACT 0x24
+#define ADXL313_REG_THRESH_INACT 0x25
+#define ADXL313_REG_TIME_INACT 0x26
#define ADXL313_REG_ACT_INACT_CTL 0x27
#define ADXL313_REG_BW_RATE 0x2C
#define ADXL313_REG_POWER_CTL 0x2D
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 74bb7cfe8a55..f7baca814da7 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -30,18 +30,22 @@
#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0)
#define ADXL313_ACT_XYZ_EN GENMASK(6, 4)
+#define ADXL313_INACT_XYZ_EN GENMASK(2, 0)
/* activity/inactivity */
enum adxl313_activity_type {
ADXL313_ACTIVITY,
+ ADXL313_INACTIVITY,
};
static const unsigned int adxl313_act_int_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY,
+ [ADXL313_INACTIVITY] = ADXL313_INT_INACTIVITY,
};
static const unsigned int adxl313_act_thresh_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT,
+ [ADXL313_INACTIVITY] = ADXL313_REG_THRESH_INACT,
};
static const struct regmap_range adxl312_readable_reg_range[] = {
@@ -254,6 +258,14 @@ static const struct iio_event_spec adxl313_fake_chan_events[] = {
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
},
+ {
+ /* inactivity */
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
};
enum adxl313_chans {
@@ -334,6 +346,15 @@ static int adxl313_read_freq_avail(struct iio_dev *indio_dev,
}
}
+static int adxl313_set_inact_time_s(struct adxl313_data *data,
+ unsigned int val_s)
+{
+ unsigned int max_boundary = 255;
+ unsigned int val = min(val_s, max_boundary);
+
+ return regmap_write(data->regmap, ADXL313_REG_TIME_INACT, val);
+}
+
static int adxl313_is_act_inact_en(struct adxl313_data *data,
enum adxl313_activity_type type)
{
@@ -346,10 +367,10 @@ static int adxl313_is_act_inact_en(struct adxl313_data *data,
return ret;
/* Check if axis for activity are enabled */
- if (type != ADXL313_ACTIVITY)
- return 0;
-
- axis_en = FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl);
+ if (type == ADXL313_ACTIVITY)
+ axis_en = FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl);
+ else
+ axis_en = FIELD_GET(ADXL313_INACT_XYZ_EN, axis_ctrl);
/* The axis are enabled, now check if specific interrupt is enabled */
ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val);
@@ -367,12 +388,14 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
{
unsigned int axis_ctrl;
unsigned int threshold;
+ unsigned int inact_time_s;
+ bool en;
int ret;
- if (type != ADXL313_ACTIVITY)
- return 0;
-
- axis_ctrl = ADXL313_ACT_XYZ_EN;
+ if (type == ADXL313_ACTIVITY)
+ axis_ctrl = ADXL313_ACT_XYZ_EN;
+ else
+ axis_ctrl = ADXL313_INACT_XYZ_EN;
ret = regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL,
axis_ctrl, cmd_en);
@@ -383,9 +406,17 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
if (ret)
return ret;
+ en = cmd_en && threshold;
+ if (type == ADXL313_INACTIVITY) {
+ ret = regmap_read(data->regmap, ADXL313_REG_TIME_INACT, &inact_time_s);
+ if (ret)
+ return ret;
+
+ en = en && inact_time_s;
+ }
+
return regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE,
- adxl313_act_int_reg[type],
- cmd_en && threshold);
+ adxl313_act_int_reg[type], en);
}
static int adxl313_read_raw(struct iio_dev *indio_dev,
@@ -474,6 +505,8 @@ static int adxl313_read_event_config(struct iio_dev *indio_dev,
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl313_is_act_inact_en(data, ADXL313_ACTIVITY);
+ case IIO_EV_DIR_FALLING:
+ return adxl313_is_act_inact_en(data, ADXL313_INACTIVITY);
default:
return -EINVAL;
}
@@ -493,6 +526,8 @@ static int adxl313_write_event_config(struct iio_dev *indio_dev,
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl313_set_act_inact_en(data, ADXL313_ACTIVITY, state);
+ case IIO_EV_DIR_FALLING:
+ return adxl313_set_act_inact_en(data, ADXL313_INACTIVITY, state);
default:
return -EINVAL;
}
@@ -507,6 +542,8 @@ static int adxl313_read_event_value(struct iio_dev *indio_dev,
{
struct adxl313_data *data = iio_priv(indio_dev);
unsigned int act_threshold;
+ unsigned int inact_threshold;
+ unsigned int inact_time_s;
int ret;
/* Measurement stays enabled, reading from regmap cache */
@@ -514,19 +551,38 @@ static int adxl313_read_event_value(struct iio_dev *indio_dev,
if (type != IIO_EV_TYPE_MAG)
return -EINVAL;
- if (info != IIO_EV_INFO_VALUE)
- return -EINVAL;
-
- switch (dir) {
- case IIO_EV_DIR_RISING:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_read(data->regmap,
+ adxl313_act_thresh_reg[ADXL313_ACTIVITY],
+ &act_threshold);
+ if (ret)
+ return ret;
+ *val = act_threshold * 15625;
+ *val2 = MICRO;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_read(data->regmap,
+ adxl313_act_thresh_reg[ADXL313_INACTIVITY],
+ &inact_threshold);
+ if (ret)
+ return ret;
+ *val = inact_threshold * 15625;
+ *val2 = MICRO;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_PERIOD:
ret = regmap_read(data->regmap,
- adxl313_act_thresh_reg[ADXL313_ACTIVITY],
- &act_threshold);
+ ADXL313_REG_TIME_INACT,
+ &inact_time_s);
if (ret)
return ret;
- *val = act_threshold * 15625;
- *val2 = MICRO;
- return IIO_VAL_FRACTIONAL;
+ *val = inact_time_s;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -550,16 +606,30 @@ static int adxl313_write_event_value(struct iio_dev *indio_dev,
if (type != IIO_EV_TYPE_MAG)
return -EINVAL;
- if (info != IIO_EV_INFO_VALUE)
- return -EINVAL;
-
- /* Scale factor 15.625 mg/LSB */
- regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625);
- switch (dir) {
- case IIO_EV_DIR_RISING:
- ret = regmap_write(data->regmap,
- adxl313_act_thresh_reg[ADXL313_ACTIVITY],
- regval);
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ /* The scale factor is 15.625 mg/LSB */
+ regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_write(data->regmap,
+ adxl313_act_thresh_reg[ADXL313_ACTIVITY],
+ regval);
+ if (ret)
+ return ret;
+ return adxl313_set_measure_en(data, true);
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_write(data->regmap,
+ adxl313_act_thresh_reg[ADXL313_INACTIVITY],
+ regval);
+ if (ret)
+ return ret;
+ return adxl313_set_measure_en(data, true);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_PERIOD:
+ ret = adxl313_set_inact_time_s(data, val);
if (ret)
return ret;
return adxl313_set_measure_en(data, true);
@@ -720,6 +790,17 @@ static int adxl313_push_event(struct iio_dev *indio_dev, int int_stat)
return ret;
}
+ if (FIELD_GET(ADXL313_INT_INACTIVITY, int_stat)) {
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_FALLING),
+ ts);
+ if (ret)
+ return ret;
+ }
+
if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) {
samples = adxl313_get_samples(data);
if (samples < 0)
@@ -890,6 +971,14 @@ int adxl313_core_probe(struct device *dev,
if (ret)
return ret;
+ ret = regmap_write(data->regmap, ADXL313_REG_TIME_INACT, 5);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADXL313_REG_THRESH_INACT, 0x4f);
+ if (ret)
+ return ret;
+
ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52);
if (ret)
return ret;
--
2.39.5
On Fri, 23 May 2025 22:35:21 +0000
Lothar Rubusch <l.rubusch@gmail.com> wrote:
> Extend the interrupt handler to process interrupts as inactivity events.
> Add functions to set threshold and period registers for inactivity. Add
> functions to enable / disable inactivity. Extend the fake iio channel to
> deal with inactivity events on x, y and z combined with AND.
>
> Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
Hi Lothar, Comments inline.
> static const struct regmap_range adxl312_readable_reg_range[] = {
> @@ -254,6 +258,14 @@ static const struct iio_event_spec adxl313_fake_chan_events[] = {
> .mask_separate = BIT(IIO_EV_INFO_ENABLE),
> .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
> },
> + {
> + /* inactivity */
Ah. I only noticed this here, but the axis for these two types of
detection should be different so awe shouldn't see them in a single
array of events.
> + .type = IIO_EV_TYPE_MAG,
> + .dir = IIO_EV_DIR_FALLING,
> + .mask_separate = BIT(IIO_EV_INFO_ENABLE),
> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
> + BIT(IIO_EV_INFO_PERIOD),
> + },
> };
> @@ -550,16 +606,30 @@ static int adxl313_write_event_value(struct iio_dev *indio_dev,
> if (type != IIO_EV_TYPE_MAG)
> return -EINVAL;
>
> - if (info != IIO_EV_INFO_VALUE)
> - return -EINVAL;
> -
> - /* Scale factor 15.625 mg/LSB */
> - regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625);
> - switch (dir) {
> - case IIO_EV_DIR_RISING:
> - ret = regmap_write(data->regmap,
> - adxl313_act_thresh_reg[ADXL313_ACTIVITY],
> - regval);
> + switch (info) {
I got lost in earlier discussion you were having with Andy on this, but
personally, if a series is going to introduce a simple test then flip
it to a switch statement later, I'd rather see the switch statement from
the start and reduce the churn a little.
> + case IIO_EV_INFO_VALUE:
> + /* The scale factor is 15.625 mg/LSB */
> + regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625);
> + switch (dir) {
> + case IIO_EV_DIR_RISING:
> + ret = regmap_write(data->regmap,
> + adxl313_act_thresh_reg[ADXL313_ACTIVITY],
> + regval);
> + if (ret)
> + return ret;
> + return adxl313_set_measure_en(data, true);
> + case IIO_EV_DIR_FALLING:
> + ret = regmap_write(data->regmap,
> + adxl313_act_thresh_reg[ADXL313_INACTIVITY],
> + regval);
> + if (ret)
> + return ret;
> + return adxl313_set_measure_en(data, true);
> + default:
> + return -EINVAL;
> + }
> + case IIO_EV_INFO_PERIOD:
> + ret = adxl313_set_inact_time_s(data, val);
> if (ret)
© 2016 - 2025 Red Hat, Inc.