From: Lothar Rubusch <l.rubusch@gmail.com>
To: jic23@kernel.org, dlechner@baylibre.com, nuno.sa@analog.com,
andy@kernel.org, corbet@lwn.net, lucas.p.stankus@gmail.com,
lars@metafoo.de, Michael.Hennerich@analog.com,
bagasdotme@gmail.com
Cc: l.rubusch@gmail.com, linux-iio@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v4 07/11] iio: accel: adxl313: add activity sensing
Date: Sun, 1 Jun 2025 17:21:35 +0000 [thread overview]
Message-ID: <20250601172139.59156-8-l.rubusch@gmail.com> (raw)
In-Reply-To: <20250601172139.59156-1-l.rubusch@gmail.com>
Add possibilities to set a threshold for activity sensing. Extend the
interrupt handler to process activity interrupts. Provide functions to set
the activity threshold and to enable/disable activity sensing. Further add
a fake channel for having x, y and z axis anded on the iio channel.
This is a preparatory patch. Some of the definitions and functions are
supposed to be extended for inactivity later on.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313_core.c | 237 ++++++++++++++++++++++++++++++-
1 file changed, 235 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 8a0b5542fb40..e647c40575ab 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -13,8 +13,10 @@
#include <linux/overflow.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/units.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
#include <linux/iio/kfifo_buf.h>
#include "adxl313.h"
@@ -25,6 +27,21 @@
#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0)
+#define ADXL313_ACT_XYZ_EN GENMASK(6, 4)
+
+/* activity/inactivity */
+enum adxl313_activity_type {
+ ADXL313_ACTIVITY,
+};
+
+static const unsigned int adxl313_act_int_reg[] = {
+ [ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY,
+};
+
+static const unsigned int adxl313_act_thresh_reg[] = {
+ [ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT,
+};
+
static const struct regmap_range adxl312_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
@@ -227,6 +244,15 @@ static const int adxl313_odr_freqs[][2] = {
}, \
}
+static const struct iio_event_spec adxl313_activity_events[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
enum adxl313_chans {
chan_x, chan_y, chan_z,
};
@@ -235,6 +261,14 @@ static const struct iio_chan_spec adxl313_channels[] = {
ADXL313_ACCEL_CHANNEL(0, chan_x, X),
ADXL313_ACCEL_CHANNEL(1, chan_y, Y),
ADXL313_ACCEL_CHANNEL(2, chan_z, Z),
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X_OR_Y_OR_Z,
+ .scan_index = -1, /* Fake channel for axis OR'ing */
+ .event_spec = adxl313_activity_events,
+ .num_event_specs = ARRAY_SIZE(adxl313_activity_events),
+ },
};
static const unsigned long adxl313_scan_masks[] = {
@@ -297,6 +331,68 @@ static int adxl313_read_freq_avail(struct iio_dev *indio_dev,
}
}
+static int adxl313_is_act_inact_en(struct adxl313_data *data,
+ enum adxl313_activity_type type)
+{
+ unsigned int axis_ctrl;
+ unsigned int regval;
+ int axis_en, int_en, ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl);
+ if (ret)
+ 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);
+
+ /* The axis are enabled, now check if specific interrupt is enabled */
+ ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val);
+ if (ret)
+ return ret;
+
+ int_en = adxl313_act_int_reg[type] & regval;
+
+ return axis_en && int_en;
+}
+
+static int adxl313_set_act_inact_en(struct adxl313_data *data,
+ enum adxl313_activity_type type,
+ bool cmd_en)
+{
+ unsigned int axis_ctrl;
+ unsigned int threshold;
+ int ret;
+
+ if (type != ADXL313_ACTIVITY)
+ return 0;
+
+ axis_ctrl = ADXL313_ACT_XYZ_EN;
+
+ ret = adxl313_set_measure_en(data, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL,
+ axis_ctrl, cmd_en);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type], &threshold);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE,
+ adxl313_act_int_reg[type],
+ cmd_en && threshold);
+ if (ret)
+ return ret;
+
+ return adxl313_set_measure_en(data, true);
+}
+
static int adxl313_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -370,6 +466,113 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
+static int adxl313_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+
+ if (type != IIO_EV_TYPE_MAG)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return adxl313_is_act_inact_en(data, ADXL313_ACTIVITY);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl313_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+
+ if (type != IIO_EV_TYPE_MAG)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return adxl313_set_act_inact_en(data, ADXL313_ACTIVITY, state);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl313_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ unsigned int act_threshold;
+ int ret;
+
+ /* Measurement stays enabled, reading from regmap cache */
+
+ if (type != IIO_EV_TYPE_MAG)
+ return -EINVAL;
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ 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;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adxl313_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ unsigned int regval;
+ int ret;
+
+ ret = adxl313_set_measure_en(data, false);
+ if (ret)
+ return ret;
+
+ 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);
+ if (ret)
+ return ret;
+ return adxl313_set_measure_en(data, true);
+ default:
+ return -EINVAL;
+ }
+}
+
static int adxl313_set_watermark(struct iio_dev *indio_dev, unsigned int value)
{
struct adxl313_data *data = iio_priv(indio_dev);
@@ -502,19 +705,32 @@ static int adxl313_fifo_push(struct iio_dev *indio_dev, int samples)
static int adxl313_push_event(struct iio_dev *indio_dev, int int_stat)
{
+ s64 ts = iio_get_time_ns(indio_dev);
struct adxl313_data *data = iio_priv(indio_dev);
int samples;
+ 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);
+ if (ret)
+ return ret;
+ }
if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) {
samples = adxl313_get_samples(data);
if (samples < 0)
return samples;
- return adxl313_fifo_push(indio_dev, samples);
+ ret = adxl313_fifo_push(indio_dev, samples);
}
/* Return error if no event data was pushed to the IIO channel. */
- return -ENOENT;
+ return ret;
}
static irqreturn_t adxl313_irq_handler(int irq, void *p)
@@ -553,6 +769,10 @@ static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg,
static const struct iio_info adxl313_info = {
.read_raw = adxl313_read_raw,
.write_raw = adxl313_write_raw,
+ .read_event_config = adxl313_read_event_config,
+ .write_event_config = adxl313_write_event_config,
+ .read_event_value = adxl313_read_event_value,
+ .write_event_value = adxl313_write_event_value,
.read_avail = adxl313_read_freq_avail,
.hwfifo_set_watermark = adxl313_set_watermark,
.debugfs_reg_access = &adxl313_reg_access,
@@ -666,6 +886,19 @@ int adxl313_core_probe(struct device *dev,
if (ret)
return ret;
+ /*
+ * Reset or configure the registers with reasonable default
+ * values. As having 0 in most cases may result in undesirable
+ * behavior if the interrupts are enabled.
+ */
+ ret = regmap_write(data->regmap, ADXL313_REG_ACT_INACT_CTL, 0x00);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52);
+ if (ret)
+ return ret;
+
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
&adxl313_buffer_ops);
if (ret)
--
2.39.5
next prev parent reply other threads:[~2025-06-01 17:22 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-01 17:21 [PATCH v4 00/11] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
2025-06-01 17:21 ` [PATCH v4 01/11] iio: accel: adxl313: add debug register Lothar Rubusch
2025-06-01 19:06 ` Andy Shevchenko
2025-06-08 15:14 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 02/11] iio: accel: adxl313: introduce channel buffer Lothar Rubusch
2025-06-01 19:08 ` Andy Shevchenko
2025-06-11 8:01 ` Lothar Rubusch
2025-06-11 8:42 ` Andy Shevchenko
2025-06-08 15:17 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 03/11] iio: accel: adxl313: make use of regmap cache Lothar Rubusch
2025-06-01 19:09 ` Andy Shevchenko
2025-06-08 15:22 ` Jonathan Cameron
2025-06-08 15:38 ` Jonathan Cameron
2025-06-11 13:48 ` Lothar Rubusch
2025-06-11 15:04 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 04/11] iio: accel: adxl313: add function to enable measurement Lothar Rubusch
2025-06-01 19:12 ` Andy Shevchenko
2025-06-08 15:27 ` Jonathan Cameron
2025-06-11 8:55 ` Lothar Rubusch
2025-06-11 15:05 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 05/11] iio: accel: adxl313: prepare interrupt handling Lothar Rubusch
2025-06-01 19:21 ` Andy Shevchenko
2025-06-11 8:26 ` Lothar Rubusch
2025-06-01 17:21 ` [PATCH v4 06/11] iio: accel: adxl313: add basic interrupt handling for FIFO watermark Lothar Rubusch
2025-06-01 19:26 ` Andy Shevchenko
2025-06-08 15:30 ` Jonathan Cameron
2025-06-08 15:44 ` Jonathan Cameron
2025-06-01 17:21 ` Lothar Rubusch [this message]
2025-06-01 19:38 ` [PATCH v4 07/11] iio: accel: adxl313: add activity sensing Andy Shevchenko
2025-06-11 14:49 ` Lothar Rubusch
2025-06-11 15:05 ` Andy Shevchenko
2025-06-11 15:15 ` Jonathan Cameron
2025-06-11 15:23 ` Andy Shevchenko
2025-06-08 16:08 ` Jonathan Cameron
2025-06-11 15:06 ` Lothar Rubusch
2025-06-11 16:47 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 08/11] iio: accel: adxl313: add inactivity sensing Lothar Rubusch
2025-06-01 19:45 ` Andy Shevchenko
2025-06-11 15:36 ` Lothar Rubusch
2025-06-11 16:52 ` Jonathan Cameron
2025-06-08 16:14 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 09/11] iio: accel: adxl313: implement power-save on inactivity Lothar Rubusch
2025-06-08 16:15 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 10/11] iio: accel: adxl313: add AC coupled activity/inactivity events Lothar Rubusch
2025-06-01 19:53 ` Andy Shevchenko
2025-06-11 17:12 ` Lothar Rubusch
2025-06-08 16:23 ` Jonathan Cameron
2025-06-11 19:58 ` Lothar Rubusch
2025-06-14 13:33 ` Jonathan Cameron
2025-06-01 17:21 ` [PATCH v4 11/11] docs: iio: add ADXL313 accelerometer Lothar Rubusch
2025-06-02 1:07 ` [PATCH v4 00/11] iio: accel: adxl313: add power-save on activity/inactivity Bagas Sanjaya
2025-06-11 20:04 ` Lothar Rubusch
2025-06-12 12:41 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250601172139.59156-8-l.rubusch@gmail.com \
--to=l.rubusch@gmail.com \
--cc=Michael.Hennerich@analog.com \
--cc=andy@kernel.org \
--cc=bagasdotme@gmail.com \
--cc=corbet@lwn.net \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=lars@metafoo.de \
--cc=linux-doc@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lucas.p.stankus@gmail.com \
--cc=nuno.sa@analog.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).