[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250622122937.156930-8-l.rubusch@gmail.com>
Date: Sun, 22 Jun 2025 12:29:36 +0000
From: Lothar Rubusch <l.rubusch@...il.com>
To: jic23@...nel.org,
dlechner@...libre.com,
nuno.sa@...log.com,
andy@...nel.org,
corbet@....net,
lucas.p.stankus@...il.com,
lars@...afoo.de,
Michael.Hennerich@...log.com,
bagasdotme@...il.com
Cc: l.rubusch@...il.com,
linux-iio@...r.kernel.org,
linux-doc@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH v6 7/8] iio: accel: adxl313: add AC coupled activity/inactivity events
Introduce AC-coupled activity and inactivity as MAG_ADAPTIVE events.
This adds a new set of threshold and duration configuration options,
ensures proper handling of event disabling, and extends the use of the
link bit to support complementary event configurations.
For example, either ACTIVITY or ACTIVITY_AC can be enabled, but only the
most recently set configuration will remain active. Disabling ACTIVITY
will have no effect if ACTIVITY_AC is currently enabled, as the event
types must match (i.e., ACTIVITY_AC must be explicitly disabled). When
either INACTIVITY or INACTIVITY_AC is enabled alongside an activity
event, the link bit is set.
With the link bit and auto-sleep enabled, activity and inactivity events
represent changes in the sensor's power-saving state and are only
triggered upon actual state transitions. Since AC coupling uses separate
bits for activity and inactivity, each can be configured independently.
For instance, ACTIVITY can be linked with INACTIVITY_AC.
If one of the linked events is disabled, the link bit is cleared. In
that case, the remaining event will no longer reflect a state transition
but will instead trigger based on periodic inactivity or whenever the
activity threshold is exceeded.
Signed-off-by: Lothar Rubusch <l.rubusch@...il.com>
---
drivers/iio/accel/adxl313_core.c | 215 ++++++++++++++++++++++++++++---
1 file changed, 200 insertions(+), 15 deletions(-)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 40c75e90e4bf..292528b457b4 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -30,20 +30,38 @@
#define ADXL313_ACT_XYZ_EN GENMASK(6, 4)
#define ADXL313_INACT_XYZ_EN GENMASK(2, 0)
+#define ADXL313_REG_ACT_ACDC_MSK BIT(7)
+#define ADXL313_REG_INACT_ACDC_MSK BIT(3)
+#define ADXL313_COUPLING_DC 0
+#define ADXL313_COUPLING_AC 1
+
/* activity/inactivity */
enum adxl313_activity_type {
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
+ ADXL313_ACTIVITY_AC,
+ ADXL313_INACTIVITY_AC,
};
static const unsigned int adxl313_act_int_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY,
[ADXL313_INACTIVITY] = ADXL313_INT_INACTIVITY,
+ [ADXL313_ACTIVITY_AC] = ADXL313_INT_ACTIVITY,
+ [ADXL313_INACTIVITY_AC] = ADXL313_INT_INACTIVITY,
};
static const unsigned int adxl313_act_thresh_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT,
[ADXL313_INACTIVITY] = ADXL313_REG_THRESH_INACT,
+ [ADXL313_ACTIVITY_AC] = ADXL313_REG_THRESH_ACT,
+ [ADXL313_INACTIVITY_AC] = ADXL313_REG_THRESH_INACT,
+};
+
+static const unsigned int adxl313_act_acdc_msk[] = {
+ [ADXL313_ACTIVITY] = ADXL313_REG_ACT_ACDC_MSK,
+ [ADXL313_INACTIVITY] = ADXL313_REG_INACT_ACDC_MSK,
+ [ADXL313_ACTIVITY_AC] = ADXL313_REG_ACT_ACDC_MSK,
+ [ADXL313_INACTIVITY_AC] = ADXL313_REG_INACT_ACDC_MSK,
};
static const struct regmap_range adxl312_readable_reg_range[] = {
@@ -255,6 +273,13 @@ static const struct iio_event_spec adxl313_activity_events[] = {
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
},
+ {
+ /* activity, AC bit set */
+ .type = IIO_EV_TYPE_MAG_ADAPTIVE,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
};
static const struct iio_event_spec adxl313_inactivity_events[] = {
@@ -266,6 +291,14 @@ static const struct iio_event_spec adxl313_inactivity_events[] = {
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD),
},
+ {
+ /* inactivity, AC bit set */
+ .type = IIO_EV_TYPE_MAG_ADAPTIVE,
+ .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 {
@@ -363,11 +396,72 @@ static int adxl313_set_inact_time_s(struct adxl313_data *data,
return regmap_write(data->regmap, ADXL313_REG_TIME_INACT, val);
}
+/**
+ * adxl313_is_act_inact_ac() - Check if AC coupling is enabled.
+ * @data: The device data.
+ * @type: The activity or inactivity type.
+ *
+ * Provide a type of activity or inactivity, combined with either AC coupling
+ * set, or default to DC coupling. This function verifies if the combination is
+ * currently enabled or not.
+ *
+ * Return: if the provided activity type has AC coupling enabled or a negative
+ * error value.
+ */
+static int adxl313_is_act_inact_ac(struct adxl313_data *data,
+ enum adxl313_activity_type type)
+{
+ unsigned int regval;
+ bool coupling;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val);
+ if (ret)
+ return ret;
+
+ coupling = adxl313_act_acdc_msk[type] & regval;
+
+ switch (type) {
+ case ADXL313_ACTIVITY:
+ case ADXL313_INACTIVITY:
+ return coupling == ADXL313_COUPLING_DC;
+ case ADXL313_ACTIVITY_AC:
+ case ADXL313_INACTIVITY_AC:
+ return coupling == ADXL313_COUPLING_AC;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl313_set_act_inact_ac(struct adxl313_data *data,
+ enum adxl313_activity_type type,
+ bool cmd_en)
+{
+ unsigned int act_inact_ac;
+
+ switch (type) {
+ case ADXL313_ACTIVITY_AC:
+ case ADXL313_INACTIVITY_AC:
+ act_inact_ac = ADXL313_COUPLING_AC && cmd_en;
+ break;
+ case ADXL313_ACTIVITY:
+ case ADXL313_INACTIVITY:
+ act_inact_ac = ADXL313_COUPLING_DC && cmd_en;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL,
+ adxl313_act_acdc_msk[type], act_inact_ac);
+}
+
static int adxl313_is_act_inact_en(struct adxl313_data *data,
enum adxl313_activity_type type)
{
unsigned int axis_ctrl;
unsigned int regval;
+ bool int_en;
int ret;
ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl);
@@ -377,10 +471,12 @@ static int adxl313_is_act_inact_en(struct adxl313_data *data,
/* Check if axis for activity are enabled */
switch (type) {
case ADXL313_ACTIVITY:
+ case ADXL313_ACTIVITY_AC:
if (!FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl))
return false;
break;
case ADXL313_INACTIVITY:
+ case ADXL313_INACTIVITY_AC:
if (!FIELD_GET(ADXL313_INACT_XYZ_EN, axis_ctrl))
return false;
break;
@@ -393,21 +489,38 @@ static int adxl313_is_act_inact_en(struct adxl313_data *data,
if (ret)
return ret;
- return adxl313_act_int_reg[type] & regval;
+ int_en = adxl313_act_int_reg[type] & regval;
+ if (!int_en)
+ return false;
+
+ /* Check if configured coupling matches provided type */
+ return adxl313_is_act_inact_ac(data, type);
}
static int adxl313_set_act_inact_linkbit(struct adxl313_data *data, bool en)
{
- int act_en, inact_en;
+ int act_en, inact_en, act_ac_en, inact_ac_en;
act_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY);
if (act_en < 0)
return act_en;
+ act_ac_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY_AC);
+ if (act_ac_en < 0)
+ return act_ac_en;
+
inact_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY);
if (inact_en < 0)
return inact_en;
+ inact_ac_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY_AC);
+ if (inact_ac_en < 0)
+ return inact_ac_en;
+
+ act_en = act_en || act_ac_en;
+
+ inact_en = inact_en || inact_ac_en;
+
return regmap_assign_bits(data->regmap, ADXL313_REG_POWER_CTL,
ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK,
en && act_en && inact_en);
@@ -433,7 +546,7 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
return 0;
/* When turning on inactivity, check if inact time is valid */
- if (type == ADXL313_INACTIVITY) {
+ if (type == ADXL313_INACTIVITY || type == ADXL313_INACTIVITY_AC) {
ret = regmap_read(data->regmap,
ADXL313_REG_TIME_INACT,
&inact_time_s);
@@ -443,6 +556,16 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
if (!inact_time_s)
return 0;
}
+ } else {
+ /*
+ * When turning off an activity, ensure that the correct
+ * coupling event is specified. This step helps prevent misuse -
+ * for example, if an AC-coupled activity is active and the
+ * current call attempts to turn off a DC-coupled activity, this
+ * inconsistency should be detected here.
+ */
+ if (adxl313_is_act_inact_ac(data, type) <= 0)
+ return 0;
}
/* Start modifying configuration registers */
@@ -453,9 +576,11 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
/* Enable axis according to the command */
switch (type) {
case ADXL313_ACTIVITY:
+ case ADXL313_ACTIVITY_AC:
axis_ctrl = ADXL313_ACT_XYZ_EN;
break;
case ADXL313_INACTIVITY:
+ case ADXL313_INACTIVITY_AC:
axis_ctrl = ADXL313_INACT_XYZ_EN;
break;
default:
@@ -466,6 +591,11 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
if (ret)
return ret;
+ /* Update AC/DC-coupling according to the command */
+ ret = adxl313_set_act_inact_ac(data, type, cmd_en);
+ if (ret)
+ return ret;
+
/* Enable the interrupt line, according to the command */
ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE,
adxl313_act_int_reg[type], cmd_en);
@@ -596,6 +726,10 @@ static int adxl313_read_event_config(struct iio_dev *indio_dev,
return adxl313_read_mag_config(data, dir,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY);
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ return adxl313_read_mag_config(data, dir,
+ ADXL313_ACTIVITY_AC,
+ ADXL313_INACTIVITY_AC);
default:
return -EINVAL;
}
@@ -615,6 +749,11 @@ static int adxl313_write_event_config(struct iio_dev *indio_dev,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
state);
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ return adxl313_write_mag_config(data, dir,
+ ADXL313_ACTIVITY_AC,
+ ADXL313_INACTIVITY_AC,
+ state);
default:
return -EINVAL;
}
@@ -740,6 +879,11 @@ static int adxl313_read_event_value(struct iio_dev *indio_dev,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
val, val2);
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ return adxl313_read_mag_value(data, dir, info,
+ ADXL313_ACTIVITY_AC,
+ ADXL313_INACTIVITY_AC,
+ val, val2);
default:
return -EINVAL;
}
@@ -760,6 +904,11 @@ static int adxl313_write_event_value(struct iio_dev *indio_dev,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
val, val2);
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ return adxl313_write_mag_value(data, dir, info,
+ ADXL313_ACTIVITY_AC,
+ ADXL313_INACTIVITY_AC,
+ val, val2);
default:
return -EINVAL;
}
@@ -906,28 +1055,64 @@ static int adxl313_fifo_push(struct iio_dev *indio_dev, int samples)
static int adxl313_push_events(struct iio_dev *indio_dev, int int_stat)
{
s64 ts = iio_get_time_ns(indio_dev);
+ struct adxl313_data *data = iio_priv(indio_dev);
+ unsigned int regval;
int ret = -ENOENT;
if (FIELD_GET(ADXL313_INT_ACTIVITY, 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_RISING),
- ts);
+ ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val);
if (ret)
return ret;
+
+ if (FIELD_GET(ADXL313_REG_ACT_ACDC_MSK, regval)) {
+ /* AC coupled */
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_MAG_ADAPTIVE,
+ IIO_EV_DIR_RISING),
+ ts);
+ if (ret)
+ return ret;
+ } else {
+ /* DC coupled, relying on THRESH */
+ 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_RISING),
+ ts);
+ if (ret)
+ 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);
+ ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val);
if (ret)
return ret;
+
+ if (FIELD_GET(ADXL313_REG_INACT_ACDC_MSK, regval)) {
+ /* AC coupled */
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_EV_TYPE_MAG_ADAPTIVE,
+ IIO_EV_DIR_FALLING),
+ ts);
+ if (ret)
+ return ret;
+ } else {
+ /* DC coupled, relying on THRESH */
+ 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;
+ }
}
return ret;
--
2.39.5
Powered by blists - more mailing lists