* [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity
@ 2025-05-18 11:13 Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 01/12] iio: accel: adxl313: add debug register Lothar Rubusch
` (11 more replies)
0 siblings, 12 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
The patch set covers the following topics:
- add debug register and regmap cache
- prepare iio channel scan_type and scan_index
- prepare interrupt handling
- implement fifo with watermark
- add activity/inactivity together with auto-sleep with link bit
- documentation
Similar situation and approach as for the ADXL345. AC/DC coupling might be
a pending option as it is quite the same as for ADXL345.
Since activity and inactivity here are implemented covering all axis, I
assumed x&y&z. Thus the driver uses a fake channel for activity/inactiviy.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
Lothar Rubusch (12):
iio: accel: adxl313: add debug register
iio: accel: adxl313: introduce channel scan_index
iio: accel: adxl313: configure scan type for buffer
iio: accel: adxl313: make use of regmap cache
iio: accel: adxl313: add function to enable measurement
iio: accel: adxl313: prepare interrupt handling
iio: accel: adxl313: add basic interrupt handling
iio: accel: adxl313: add FIFO watermark
iio: accel: adxl313: add activity sensing
iio: accel: adxl313: add inactivity sensing
iio: accel: adxl313: implement power-save on inactivity
docs: iio: add ADXL313 accelerometer
Documentation/iio/adxl313.rst | 196 +++++++++
Documentation/iio/index.rst | 1 +
drivers/iio/accel/adxl313.h | 35 +-
drivers/iio/accel/adxl313_core.c | 658 ++++++++++++++++++++++++++++++-
drivers/iio/accel/adxl313_i2c.c | 6 +
drivers/iio/accel/adxl313_spi.c | 6 +
6 files changed, 893 insertions(+), 9 deletions(-)
create mode 100644 Documentation/iio/adxl313.rst
--
2.39.5
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v1 01/12] iio: accel: adxl313: add debug register
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index Lothar Rubusch
` (10 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Add iio debug register for general sensor debugging.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313_core.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 4de0a41bd679..2f26da5857d4 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -321,10 +321,21 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
+static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(data->regmap, reg, readval);
+ return regmap_write(data->regmap, reg, writeval);
+}
+
static const struct iio_info adxl313_info = {
.read_raw = adxl313_read_raw,
.write_raw = adxl313_write_raw,
.read_avail = adxl313_read_freq_avail,
+ .debugfs_reg_access = &adxl313_reg_access,
};
static int adxl313_setup(struct device *dev, struct adxl313_data *data,
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 01/12] iio: accel: adxl313: add debug register Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 10:46 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 03/12] iio: accel: adxl313: configure scan type for buffer Lothar Rubusch
` (9 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Add a scan_mask and scan_index to the iio channel. The scan_index
prepares the buffer usage.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313_core.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 2f26da5857d4..000f59e2db87 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -171,9 +171,10 @@ static const int adxl313_odr_freqs[][2] = {
[9] = { 3200, 0 },
};
-#define ADXL313_ACCEL_CHANNEL(index, axis) { \
+#define ADXL313_ACCEL_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
- .address = index, \
+ .scan_index = (index), \
+ .address = (reg), \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
@@ -187,10 +188,19 @@ static const int adxl313_odr_freqs[][2] = {
}, \
}
+enum adxl313_chans {
+ chan_x, chan_y, chan_z
+};
+
static const struct iio_chan_spec adxl313_channels[] = {
- ADXL313_ACCEL_CHANNEL(0, X),
- ADXL313_ACCEL_CHANNEL(1, Y),
- ADXL313_ACCEL_CHANNEL(2, Z),
+ ADXL313_ACCEL_CHANNEL(0, chan_x, X),
+ ADXL313_ACCEL_CHANNEL(1, chan_y, Y),
+ ADXL313_ACCEL_CHANNEL(2, chan_z, Z),
+};
+
+static const unsigned long adxl313_scan_masks[] = {
+ BIT(chan_x) | BIT(chan_y) | BIT(chan_z),
+ 0
};
static int adxl313_set_odr(struct adxl313_data *data,
@@ -419,6 +429,7 @@ int adxl313_core_probe(struct device *dev,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl313_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl313_channels);
+ indio_dev->available_scan_masks = adxl313_scan_masks;
ret = adxl313_setup(dev, data, setup);
if (ret) {
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 03/12] iio: accel: adxl313: configure scan type for buffer
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 01/12] iio: accel: adxl313: add debug register Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 04/12] iio: accel: adxl313: make use of regmap cache Lothar Rubusch
` (8 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
According to the datasheet the ADXL313 uses 13 bit in full resolution.
Also, add signedness, storage bits and endianness.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313_core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 000f59e2db87..ab889d50b4d4 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -184,7 +184,10 @@ static const int adxl313_odr_freqs[][2] = {
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_type = { \
+ .sign = 's', \
.realbits = 13, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
}, \
}
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 04/12] iio: accel: adxl313: make use of regmap cache
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (2 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 03/12] iio: accel: adxl313: configure scan type for buffer Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 05/12] iio: accel: adxl313: add function to enable measurement Lothar Rubusch
` (7 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Setup regmap cache to cache register configuration. This is a preparatory
step for follow up patches, to allow easy acces to the cached
configuration.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 2 ++
drivers/iio/accel/adxl313_core.c | 17 +++++++++++++++++
drivers/iio/accel/adxl313_i2c.c | 6 ++++++
drivers/iio/accel/adxl313_spi.c | 6 ++++++
4 files changed, 31 insertions(+)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 72f624af4686..fc937bdf83b6 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -54,6 +54,8 @@ extern const struct regmap_access_table adxl312_writable_regs_table;
extern const struct regmap_access_table adxl313_writable_regs_table;
extern const struct regmap_access_table adxl314_writable_regs_table;
+bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg);
+
enum adxl313_device_type {
ADXL312,
ADXL313,
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index ab889d50b4d4..6e0c5704e333 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -46,6 +46,23 @@ const struct regmap_access_table adxl314_readable_regs_table = {
};
EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313);
+bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADXL313_REG_DATA_AXIS(0):
+ case ADXL313_REG_DATA_AXIS(1):
+ case ADXL313_REG_DATA_AXIS(2):
+ case ADXL313_REG_DATA_AXIS(3):
+ case ADXL313_REG_DATA_AXIS(4):
+ case ADXL313_REG_DATA_AXIS(5):
+ case ADXL313_REG_FIFO_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(adxl313_is_volatile_reg, "IIO_ADXL313");
+
static int adxl312_check_id(struct device *dev,
struct adxl313_data *data)
{
diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c
index a4cf0cf2c5aa..e8636e8ab14f 100644
--- a/drivers/iio/accel/adxl313_i2c.c
+++ b/drivers/iio/accel/adxl313_i2c.c
@@ -21,6 +21,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl312_readable_regs_table,
.wr_table = &adxl312_writable_regs_table,
.max_register = 0x39,
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
[ADXL313] = {
.reg_bits = 8,
@@ -28,6 +30,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl313_readable_regs_table,
.wr_table = &adxl313_writable_regs_table,
.max_register = 0x39,
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
[ADXL314] = {
.reg_bits = 8,
@@ -35,6 +39,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl314_readable_regs_table,
.wr_table = &adxl314_writable_regs_table,
.max_register = 0x39,
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
};
diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
index 9a16b40bff34..68e323e81aeb 100644
--- a/drivers/iio/accel/adxl313_spi.c
+++ b/drivers/iio/accel/adxl313_spi.c
@@ -24,6 +24,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
[ADXL313] = {
.reg_bits = 8,
@@ -33,6 +35,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
[ADXL314] = {
.reg_bits = 8,
@@ -42,6 +46,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
+ .volatile_reg = adxl313_is_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
},
};
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 05/12] iio: accel: adxl313: add function to enable measurement
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (3 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 04/12] iio: accel: adxl313: make use of regmap cache Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling Lothar Rubusch
` (6 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Add a function to enable measurement. The data-sheet recomments turning of
measurement while modifying certain config registers. This is a preparatory
step.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 3 ++-
drivers/iio/accel/adxl313_core.c | 12 +++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index fc937bdf83b6..ba5b5d53a0ea 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -36,8 +36,9 @@
#define ADXL313_RATE_MSK GENMASK(3, 0)
#define ADXL313_RATE_BASE 6
-#define ADXL313_POWER_CTL_MSK GENMASK(3, 2)
+#define ADXL313_POWER_CTL_MSK BIT(3)
#define ADXL313_MEASUREMENT_MODE BIT(3)
+#define ADXL313_MEASUREMENT_STANDBY 0x00
#define ADXL313_RANGE_MSK GENMASK(1, 0)
#define ADXL313_RANGE_MAX 3
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 6e0c5704e333..244fb2ec0b79 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -63,6 +63,14 @@ bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_NS_GPL(adxl313_is_volatile_reg, "IIO_ADXL313");
+static int adxl313_set_measure_en(struct adxl313_data *data, bool en)
+{
+ unsigned int val = en ? ADXL313_MEASUREMENT_MODE : ADXL313_MEASUREMENT_STANDBY;
+
+ return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
+ ADXL313_POWER_CTL_MSK, val);
+}
+
static int adxl312_check_id(struct device *dev,
struct adxl313_data *data)
{
@@ -410,9 +418,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
}
/* Enables measurement mode */
- return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
- ADXL313_POWER_CTL_MSK,
- ADXL313_MEASUREMENT_MODE);
+ return adxl313_set_measure_en(data, true);
}
/**
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (4 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 05/12] iio: accel: adxl313: add function to enable measurement Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 11:48 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 07/12] iio: accel: adxl313: add basic " Lothar Rubusch
` (5 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Evaluate the devicetree property for an optional interrupt line, and
configure the interrupt mapping accordingly. When no interrupt line
is defined in the devicetree, keep the FIFO in bypass mode as before.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 8 ++++++++
drivers/iio/accel/adxl313_core.c | 32 ++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index ba5b5d53a0ea..c5673f1934fb 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -21,7 +21,9 @@
#define ADXL313_REG_ACT_INACT_CTL 0x27
#define ADXL313_REG_BW_RATE 0x2C
#define ADXL313_REG_POWER_CTL 0x2D
+#define ADXL313_REG_INT_ENABLE 0x2E
#define ADXL313_REG_INT_MAP 0x2F
+#define ADXL313_REG_INT_SOURCE 0x30
#define ADXL313_REG_DATA_FORMAT 0x31
#define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2))
#define ADXL313_REG_FIFO_CTL 0x38
@@ -47,6 +49,11 @@
#define ADXL313_SPI_3WIRE BIT(6)
#define ADXL313_I2C_DISABLE BIT(6)
+#define ADXL313_REG_FIFO_CTL_MODE_MSK GENMASK(7, 6)
+
+#define ADXL313_FIFO_BYPASS 0
+#define ADXL313_FIFO_STREAM 2
+
extern const struct regmap_access_table adxl312_readable_regs_table;
extern const struct regmap_access_table adxl313_readable_regs_table;
extern const struct regmap_access_table adxl314_readable_regs_table;
@@ -67,6 +74,7 @@ struct adxl313_data {
struct regmap *regmap;
const struct adxl313_chip_info *chip_info;
struct mutex lock; /* lock to protect transf_buf */
+ int irq;
__le16 transf_buf __aligned(IIO_DMA_MINALIGN);
};
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 244fb2ec0b79..05e99708c2c1 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -8,11 +8,17 @@
*/
#include <linux/bitfield.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include "adxl313.h"
+#define ADXL313_INT_NONE 0
+#define ADXL313_INT1 1
+#define ADXL313_INT2 2
+
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)),
@@ -438,6 +444,8 @@ int adxl313_core_probe(struct device *dev,
{
struct adxl313_data *data;
struct iio_dev *indio_dev;
+ unsigned int regval;
+ u8 int_line;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
@@ -463,6 +471,30 @@ int adxl313_core_probe(struct device *dev,
return ret;
}
+ int_line = ADXL313_INT1;
+ data->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
+ if (data->irq < 0) {
+ int_line = ADXL313_INT2;
+ data->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
+ if (data->irq < 0)
+ int_line = ADXL313_INT_NONE;
+ }
+
+ if (int_line) {
+ /* FIFO_STREAM mode */
+ regval = int_line == ADXL313_INT2 ? 0xff : 0;
+ ret = regmap_write(data->regmap, ADXL313_REG_INT_MAP, regval);
+ if (ret)
+ return ret;
+ } else {
+ /* FIFO_BYPASSED mode */
+ ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
+ FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK,
+ ADXL313_FIFO_BYPASS));
+ if (ret)
+ return ret;
+ }
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 07/12] iio: accel: adxl313: add basic interrupt handling
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (5 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 11:59 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 08/12] iio: accel: adxl313: add FIFO watermark Lothar Rubusch
` (4 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Prepare the interrupt handler. Add register entries to evaluate the
incoming interrupt. Add functions to clear status registers and reset the
FIFO.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 16 ++++
drivers/iio/accel/adxl313_core.c | 133 +++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index c5673f1934fb..4cb1fe1f2616 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -49,11 +49,25 @@
#define ADXL313_SPI_3WIRE BIT(6)
#define ADXL313_I2C_DISABLE BIT(6)
+#define ADXL313_INT_OVERRUN BIT(0)
+#define ADXL313_INT_WATERMARK BIT(1)
+#define ADXL313_INT_INACTIVITY BIT(3)
+#define ADXL313_INT_ACTIVITY BIT(4)
+#define ADXL313_INT_DREADY BIT(7)
+
+/* FIFO entries: how many values are stored in the FIFO */
+#define ADXL313_REG_FIFO_STATUS_ENTRIES_MSK GENMASK(5, 0)
+/* FIFO samples: number of samples needed for watermark (FIFO mode) */
+#define ADXL313_REG_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0)
#define ADXL313_REG_FIFO_CTL_MODE_MSK GENMASK(7, 6)
#define ADXL313_FIFO_BYPASS 0
#define ADXL313_FIFO_STREAM 2
+#define ADXL313_FIFO_SIZE 32
+
+#define ADXL313_NUM_AXIS 3
+
extern const struct regmap_access_table adxl312_readable_regs_table;
extern const struct regmap_access_table adxl313_readable_regs_table;
extern const struct regmap_access_table adxl314_readable_regs_table;
@@ -75,7 +89,9 @@ struct adxl313_data {
const struct adxl313_chip_info *chip_info;
struct mutex lock; /* lock to protect transf_buf */
int irq;
+ u8 fifo_mode;
__le16 transf_buf __aligned(IIO_DMA_MINALIGN);
+ __le16 fifo_buf[ADXL313_NUM_AXIS * ADXL313_FIFO_SIZE + 1];
};
struct adxl313_chip_info {
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 05e99708c2c1..574962a8f35d 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -13,12 +13,20 @@
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+
#include "adxl313.h"
#define ADXL313_INT_NONE 0
#define ADXL313_INT1 1
#define ADXL313_INT2 2
+#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0)
+
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)),
@@ -62,6 +70,7 @@ bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg)
case ADXL313_REG_DATA_AXIS(4):
case ADXL313_REG_DATA_AXIS(5):
case ADXL313_REG_FIFO_STATUS:
+ case ADXL313_REG_INT_SOURCE:
return true;
default:
return false;
@@ -365,6 +374,118 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
+static int adxl313_get_samples(struct adxl313_data *data)
+{
+ unsigned int regval = 0;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_FIFO_STATUS, ®val);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(ADXL313_REG_FIFO_STATUS_ENTRIES_MSK, regval);
+}
+
+static int adxl313_set_fifo(struct adxl313_data *data)
+{
+ unsigned int int_line;
+ int ret;
+
+ ret = adxl313_set_measure_en(data, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_INT_MAP, &int_line);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
+ FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK,
+ data->fifo_mode));
+
+ return adxl313_set_measure_en(data, true);
+}
+
+static int adxl313_fifo_transfer(struct adxl313_data *data, int samples)
+{
+ size_t count;
+ int i;
+ int ret;
+
+ ret = 0;
+ count = sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS;
+ for (i = 0; i < samples; i++) {
+ ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE,
+ data->fifo_buf + (i * count / 2), count);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static void adxl313_fifo_reset(struct adxl313_data *data)
+{
+ int regval;
+ int samples;
+
+ adxl313_set_measure_en(data, false);
+
+ /* clear samples */
+ samples = adxl313_get_samples(data);
+ if (samples)
+ adxl313_fifo_transfer(data, samples);
+
+ /* clear interrupt register */
+ regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, ®val);
+
+ adxl313_set_measure_en(data, true);
+}
+
+static int adxl313_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+
+ data->fifo_mode = ADXL313_FIFO_STREAM;
+ return adxl313_set_fifo(data);
+}
+
+static int adxl313_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int ret;
+
+ data->fifo_mode = ADXL313_FIFO_BYPASS;
+ ret = adxl313_set_fifo(data);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0);
+}
+
+static const struct iio_buffer_setup_ops adxl313_buffer_ops = {
+ .postenable = adxl313_buffer_postenable,
+ .predisable = adxl313_buffer_predisable,
+};
+
+static irqreturn_t adxl313_irq_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int int_stat;
+
+ if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
+ return IRQ_NONE;
+
+ if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
+ goto err;
+
+ return IRQ_HANDLED;
+err:
+ adxl313_fifo_reset(data);
+
+ return IRQ_HANDLED;
+}
+
static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
@@ -486,6 +607,18 @@ int adxl313_core_probe(struct device *dev,
ret = regmap_write(data->regmap, ADXL313_REG_INT_MAP, regval);
if (ret)
return ret;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &adxl313_buffer_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, data->irq, NULL,
+ &adxl313_irq_handler,
+ IRQF_SHARED | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
} else {
/* FIFO_BYPASSED mode */
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 08/12] iio: accel: adxl313: add FIFO watermark
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (6 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 07/12] iio: accel: adxl313: add basic " Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 09/12] iio: accel: adxl313: add activity sensing Lothar Rubusch
` (3 subsequent siblings)
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Add FIFO watermark configuration and evaluation. Let a watermark to be
configured. Evaluate the interrupt accordingly. Read out the FIFO content
and push the values to the IIO channel.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 1 +
drivers/iio/accel/adxl313_core.c | 60 ++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 4cb1fe1f2616..9c7aedf7da7a 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -89,6 +89,7 @@ struct adxl313_data {
const struct adxl313_chip_info *chip_info;
struct mutex lock; /* lock to protect transf_buf */
int irq;
+ u8 watermark;
u8 fifo_mode;
__le16 transf_buf __aligned(IIO_DMA_MINALIGN);
__le16 fifo_buf[ADXL313_NUM_AXIS * ADXL313_FIFO_SIZE + 1];
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 574962a8f35d..0a93af89ba19 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -374,6 +374,25 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
+static int adxl313_set_watermark(struct iio_dev *indio_dev, unsigned int value)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ const unsigned int fifo_mask = 0x1f, watermark_mask = 0x02;
+ int ret;
+
+ value = min(value, ADXL313_FIFO_SIZE - 1);
+
+ ret = regmap_update_bits(data->regmap, ADXL313_REG_FIFO_CTL,
+ fifo_mask, value);
+ if (ret)
+ return ret;
+
+ data->watermark = value;
+
+ return regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
+ watermark_mask, ADXL313_INT_WATERMARK);
+}
+
static int adxl313_get_samples(struct adxl313_data *data)
{
unsigned int regval = 0;
@@ -400,6 +419,8 @@ static int adxl313_set_fifo(struct adxl313_data *data)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
+ FIELD_PREP(ADXL313_REG_FIFO_CTL_SAMPLES_MSK,
+ data->watermark) |
FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK,
data->fifo_mode));
@@ -467,6 +488,41 @@ static const struct iio_buffer_setup_ops adxl313_buffer_ops = {
.predisable = adxl313_buffer_predisable,
};
+static int adxl313_fifo_push(struct iio_dev *indio_dev, int samples)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int i, ret;
+
+ if (samples <= 0)
+ return -EINVAL;
+
+ ret = adxl313_fifo_transfer(data, samples);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ADXL313_NUM_AXIS * samples; i += ADXL313_NUM_AXIS)
+ iio_push_to_buffers(indio_dev, &data->fifo_buf[i]);
+
+ return 0;
+}
+
+static int adxl313_push_event(struct iio_dev *indio_dev, int int_stat)
+{
+ struct adxl313_data *data = iio_priv(indio_dev);
+ int samples;
+ int ret = -ENOENT;
+
+ if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) {
+ samples = adxl313_get_samples(data);
+ if (samples < 0)
+ return -EINVAL;
+
+ ret = adxl313_fifo_push(indio_dev, samples);
+ }
+
+ return ret;
+}
+
static irqreturn_t adxl313_irq_handler(int irq, void *p)
{
struct iio_dev *indio_dev = p;
@@ -476,6 +532,9 @@ static irqreturn_t adxl313_irq_handler(int irq, void *p)
if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
return IRQ_NONE;
+ if (adxl313_push_event(indio_dev, int_stat))
+ goto err;
+
if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
goto err;
@@ -500,6 +559,7 @@ static const struct iio_info adxl313_info = {
.read_raw = adxl313_read_raw,
.write_raw = adxl313_write_raw,
.read_avail = adxl313_read_freq_avail,
+ .hwfifo_set_watermark = adxl313_set_watermark,
.debugfs_reg_access = &adxl313_reg_access,
};
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 09/12] iio: accel: adxl313: add activity sensing
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (7 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 08/12] iio: accel: adxl313: add FIFO watermark Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 12:15 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing Lothar Rubusch
` (2 subsequent siblings)
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
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 | 252 +++++++++++++++++++++++++++++++
1 file changed, 252 insertions(+)
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 0a93af89ba19..49914ccc92cf 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -27,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)),
@@ -231,6 +246,16 @@ static const int adxl313_odr_freqs[][2] = {
}, \
}
+static const struct iio_event_spec adxl313_fake_chan_events[] = {
+ {
+ /* activity */
+ .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
};
@@ -239,6 +264,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_AND_Y_AND_Z,
+ .scan_index = -1, /* Fake channel for axis AND'ing */
+ .event_spec = adxl313_fake_chan_events,
+ .num_event_specs = ARRAY_SIZE(adxl313_fake_chan_events),
+ },
};
static const unsigned long adxl313_scan_masks[] = {
@@ -301,6 +334,67 @@ 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,
+ bool *en)
+{
+ unsigned int axis_ctrl;
+ unsigned int regval;
+ int ret;
+
+ *en = false;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl);
+ if (ret)
+ return ret;
+
+ if (type == ADXL313_ACTIVITY)
+ *en = FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl);
+
+ if (*en) {
+ ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val);
+ if (ret)
+ return ret;
+
+ *en = adxl313_act_int_reg[type] & regval;
+ }
+
+ return 0;
+}
+
+static int adxl313_set_act_inact_en(struct adxl313_data *data,
+ enum adxl313_activity_type type,
+ bool cmd_en)
+{
+ unsigned int axis_ctrl = 0;
+ unsigned int threshold;
+ bool en;
+ int ret;
+
+ if (type == ADXL313_ACTIVITY)
+ axis_ctrl = ADXL313_ACT_XYZ_EN;
+
+ ret = regmap_update_bits(data->regmap,
+ ADXL313_REG_ACT_INACT_CTL,
+ axis_ctrl,
+ cmd_en ? 0xff : 0x00);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type], &threshold);
+ if (ret)
+ return ret;
+
+ en = false;
+
+ if (type == ADXL313_ACTIVITY)
+ en = cmd_en && threshold;
+
+ return regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
+ adxl313_act_int_reg[type],
+ en ? adxl313_act_int_reg[type] : 0);
+}
+
static int adxl313_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -374,6 +468,135 @@ 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);
+ bool int_en;
+ int ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = adxl313_is_act_inact_en(data,
+ ADXL313_ACTIVITY,
+ &int_en);
+ if (ret)
+ return ret;
+ return int_en;
+ default:
+ return -EINVAL;
+ }
+ 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);
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return adxl313_set_act_inact_en(data,
+ ADXL313_ACTIVITY,
+ state);
+ default:
+ return -EINVAL;
+ }
+ 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 */
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
+ 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 = 1000000;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ 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;
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ /* The scale factor is 15.625 mg/LSB */
+ regval = DIV_ROUND_CLOSEST(1000000 * 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;
+ }
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
static int adxl313_set_watermark(struct iio_dev *indio_dev, unsigned int value)
{
struct adxl313_data *data = iio_priv(indio_dev);
@@ -508,10 +731,22 @@ 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_AND_Y_AND_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)
@@ -558,6 +793,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,
@@ -668,6 +907,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, 0);
+ 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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (8 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 09/12] iio: accel: adxl313: add activity sensing Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 12:17 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 12/12] docs: iio: add ADXL313 accelerometer Lothar Rubusch
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
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 | 98 +++++++++++++++++++++++++++++++-
2 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 9c7aedf7da7a..1398ac54d19b 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 49914ccc92cf..46ca30abc7fd 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -28,18 +28,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,
bool *en)
@@ -350,6 +371,8 @@ static int adxl313_is_act_inact_en(struct adxl313_data *data,
if (type == ADXL313_ACTIVITY)
*en = FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl);
+ else
+ *en = FIELD_GET(ADXL313_INACT_XYZ_EN, axis_ctrl);
if (*en) {
ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val);
@@ -366,13 +389,16 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
enum adxl313_activity_type type,
bool cmd_en)
{
- unsigned int axis_ctrl = 0;
+ unsigned int axis_ctrl;
unsigned int threshold;
+ unsigned int inact_time_s;
bool en;
int ret;
if (type == ADXL313_ACTIVITY)
axis_ctrl = ADXL313_ACT_XYZ_EN;
+ else
+ axis_ctrl = ADXL313_INACT_XYZ_EN;
ret = regmap_update_bits(data->regmap,
ADXL313_REG_ACT_INACT_CTL,
@@ -387,8 +413,15 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
en = false;
- if (type == ADXL313_ACTIVITY)
+ if (type == ADXL313_ACTIVITY) {
en = cmd_en && threshold;
+ } else {
+ ret = regmap_read(data->regmap, ADXL313_REG_TIME_INACT, &inact_time_s);
+ if (ret)
+ return ret;
+
+ en = cmd_en && threshold && inact_time_s;
+ }
return regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
adxl313_act_int_reg[type],
@@ -487,6 +520,13 @@ static int adxl313_read_event_config(struct iio_dev *indio_dev,
if (ret)
return ret;
return int_en;
+ case IIO_EV_DIR_FALLING:
+ ret = adxl313_is_act_inact_en(data,
+ ADXL313_INACTIVITY,
+ &int_en);
+ if (ret)
+ return ret;
+ return int_en;
default:
return -EINVAL;
}
@@ -510,6 +550,10 @@ static int adxl313_write_event_config(struct iio_dev *indio_dev,
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;
}
@@ -527,6 +571,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 */
@@ -545,9 +591,26 @@ static int adxl313_read_event_value(struct iio_dev *indio_dev,
*val = act_threshold * 15625;
*val2 = 1000000;
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 = 1000000;
+ return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
+ case IIO_EV_INFO_PERIOD:
+ ret = regmap_read(data->regmap,
+ ADXL313_REG_TIME_INACT,
+ &inact_time_s);
+ if (ret)
+ return ret;
+ *val = inact_time_s;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -586,9 +649,21 @@ static int adxl313_write_event_value(struct iio_dev *indio_dev,
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);
default:
return -EINVAL;
}
@@ -747,6 +822,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)
@@ -916,6 +1002,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
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (9 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
2025-05-19 12:19 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 12/12] docs: iio: add ADXL313 accelerometer Lothar Rubusch
11 siblings, 1 reply; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Link activity and inactivity to indicate the internal power-saving state.
Add auto-sleep to be linked to inactivity.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/iio/accel/adxl313.h | 3 +++
drivers/iio/accel/adxl313_core.c | 29 ++++++++++++++++++++++++++---
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 1398ac54d19b..eafdf8f57816 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -41,6 +41,9 @@
#define ADXL313_RATE_BASE 6
#define ADXL313_POWER_CTL_MSK BIT(3)
+#define ADXL313_POWER_CTL_INACT_MSK GENMASK(5, 4)
+#define ADXL313_POWER_CTL_LINK BIT(5)
+#define ADXL313_POWER_CTL_AUTO_SLEEP BIT(4)
#define ADXL313_MEASUREMENT_MODE BIT(3)
#define ADXL313_MEASUREMENT_STANDBY 0x00
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 46ca30abc7fd..aabb4a1b9b9b 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -392,6 +392,7 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
unsigned int axis_ctrl;
unsigned int threshold;
unsigned int inact_time_s;
+ bool act_en, inact_en;
bool en;
int ret;
@@ -423,9 +424,31 @@ static int adxl313_set_act_inact_en(struct adxl313_data *data,
en = cmd_en && threshold && inact_time_s;
}
- return regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
- adxl313_act_int_reg[type],
- en ? adxl313_act_int_reg[type] : 0);
+ ret = regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
+ adxl313_act_int_reg[type],
+ en ? adxl313_act_int_reg[type] : 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Advanced power saving: Set sleep and link bit only when ACT and INACT
+ * are set. Check enable against regmap cached values.
+ */
+ ret = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY, &act_en);
+ if (ret)
+ return ret;
+
+ ret = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY, &inact_en);
+ if (ret)
+ return ret;
+
+ en = en && act_en && inact_en;
+
+ return regmap_update_bits(data->regmap,
+ ADXL313_REG_POWER_CTL,
+ ADXL313_POWER_CTL_INACT_MSK,
+ en ? (ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK)
+ : 0);
}
static int adxl313_read_raw(struct iio_dev *indio_dev,
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v1 12/12] docs: iio: add ADXL313 accelerometer
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
` (10 preceding siblings ...)
2025-05-18 11:13 ` [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity Lothar Rubusch
@ 2025-05-18 11:13 ` Lothar Rubusch
11 siblings, 0 replies; 19+ messages in thread
From: Lothar Rubusch @ 2025-05-18 11:13 UTC (permalink / raw)
To: jic23, dlechner, nuno.sa, andy, corbet, lucas.p.stankus, lars,
Michael.Hennerich
Cc: linux-iio, linux-doc, linux-kernel, Lothar Rubusch
Add documentation for the ADXL313 accelerometer driver.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
Documentation/iio/adxl313.rst | 196 ++++++++++++++++++++++++++++++++++
Documentation/iio/index.rst | 1 +
2 files changed, 197 insertions(+)
create mode 100644 Documentation/iio/adxl313.rst
diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst
new file mode 100644
index 000000000000..8c4e2d141594
--- /dev/null
+++ b/Documentation/iio/adxl313.rst
@@ -0,0 +1,196 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+ADXL313 driver
+===============
+
+This driver supports Analog Device's ADXL313 on SPI/I2C bus.
+
+1. Supported devices
+====================
+
+* `ADXL313 <https://www.analog.com/ADXL313>`_
+
+The ADXL313is a low noise density, low power, 3-axis accelerometer with
+selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and
+±4 g ranges.
+
+2. Device attributes
+====================
+
+Accelerometer measurements are always provided.
+
+Temperature data are also provided. This data can be used to monitor the
+internal system temperature or to improve the temperature stability of the
+device via calibration.
+
+Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
+where X is the IIO index of the device. Under these folders reside a set of
+device files, depending on the characteristics and features of the hardware
+device in questions. These files are consistently generalized and documented in
+the IIO ABI documentation.
+
+The following tables show the adxl313 related device files, found in the
+specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+
++---------------------------------------------------+----------------------------------------------------------+
+| 3-Axis Accelerometer related device files | Description |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_scale | Scale for the accelerometer channels. |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_x_raw | Raw X-axis accelerometer channel value. |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_y_calibbias | y-axis acceleration offset correction |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
++---------------------------------------------------+----------------------------------------------------------+
+| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
++---------------------------------------------------+----------------------------------------------------------+
+
++---------------------------------------+----------------------------------------------+
+| Miscellaneous device files | Description |
++---------------------------------------+----------------------------------------------+
+| name | Name of the IIO device. |
++---------------------------------------+----------------------------------------------+
+| in_accel_sampling_frequency | Currently selected sample rate. |
++---------------------------------------+----------------------------------------------+
+| in_accel_sampling_frequency_available | Available sampling frequency configurations. |
++---------------------------------------+----------------------------------------------+
+
+Channels processed values
+-------------------------
+
+A channel value can be read from its _raw attribute. The value returned is the
+raw value as reported by the devices. To get the processed value of the channel,
+apply the following formula:
+
+.. code-block:: bash
+
+ processed value = (_raw + _offset) * _scale
+
+Where _offset and _scale are device attributes. If no _offset attribute is
+present, simply assume its value is 0.
+
+The ADXL313 driver offers data for a single types of channels, the table below
+shows the measurement units for the processed value, which are defined by the
+IIO framework:
+
++-------------------------------------+---------------------------+
+| Channel type | Measurement unit |
++-------------------------------------+---------------------------+
+| Acceleration on X, Y, and Z axis | Meters per Second squared |
++-------------------------------------+---------------------------+
+
+Usage examples
+--------------
+
+Show device name:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> cat name
+ adxl313
+
+Show accelerometer channels value:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
+ 2
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
+ -57
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
+ 2
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
+ 0.009576806
+
+- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.0191536 m/s^2
+- Y-axis acceleration = in_accel_y_raw * in_accel_scale = -0.5458779 m/s^2
+- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 0.0191536 m/s^2
+
+Set calibration offset for accelerometer channels. Note, the calibration will be
+rounded according to the graduation of LSB units:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
+ 0
+
+ root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
+ 48
+
+Set sampling frequency:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency
+ 100.000000
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency_available
+ 6.250000 12.500000 25.000000 50.000000 100.000000 200.000000 400.000000 800.000000 1600.000000 3200.000000
+
+ root:/sys/bus/iio/devices/iio:device0> echo 400 > in_accel_sampling_frequency
+ root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency
+ 400.000000
+
+3. Device buffers
+=================
+
+This driver supports IIO buffers.
+
+All devices support retrieving the raw acceleration measurements using buffers.
+
+Usage examples
+--------------
+
+Select channels for buffer read:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en
+ root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en
+ root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en
+
+Set the number of samples to be stored in the buffer:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
+
+Enable buffer readings:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
+
+Obtain buffered data:
+
+.. code-block:: bash
+
+ root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
+ ...
+ 000000d0 01 fc 31 00 c7 ff 03 fc 31 00 c7 ff 04 fc 33 00 |..1.....1.....3.|
+ 000000e0 c8 ff 03 fc 32 00 c5 ff ff fc 32 00 c7 ff 0a fc |....2.....2.....|
+ 000000f0 30 00 c8 ff 06 fc 33 00 c7 ff 01 fc 2f 00 c8 ff |0.....3...../...|
+ 00000100 02 fc 32 00 c6 ff 04 fc 33 00 c8 ff 05 fc 33 00 |..2.....3.....3.|
+ 00000110 ca ff 02 fc 31 00 c7 ff 02 fc 30 00 c9 ff 09 fc |....1.....0.....|
+ 00000120 35 00 c9 ff 08 fc 35 00 c8 ff 02 fc 31 00 c5 ff |5.....5.....1...|
+ 00000130 03 fc 32 00 c7 ff 04 fc 32 00 c7 ff 02 fc 31 00 |..2.....2.....1.|
+ 00000140 c7 ff 08 fc 30 00 c7 ff 02 fc 32 00 c5 ff ff fc |....0.....2.....|
+ 00000150 31 00 c5 ff 04 fc 31 00 c8 ff 03 fc 32 00 c8 ff |1.....1.....2...|
+ 00000160 01 fc 31 00 c7 ff 05 fc 31 00 c3 ff 04 fc 31 00 |..1.....1.....1.|
+ 00000170 c5 ff 04 fc 30 00 c7 ff 03 fc 31 00 c9 ff 03 fc |....0.....1.....|
+ ...
+
+See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
+data is structured.
+
+4. IIO Interfacing Tools
+========================
+
+See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO
+interfacing tools.
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 2d6afc5a8ed5..c106402a91f7 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers
adis16475
adis16480
adis16550
+ adxl313
adxl380
bno055
ep93xx_adc
--
2.39.5
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index
2025-05-18 11:13 ` [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index Lothar Rubusch
@ 2025-05-19 10:46 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 10:46 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:11AM +0000, Lothar Rubusch wrote:
> Add a scan_mask and scan_index to the iio channel. The scan_index
> prepares the buffer usage.
...
> +enum adxl313_chans {
> + chan_x, chan_y, chan_z
Please, keep trailing comma in this (when it's multi-line assignment) case.
> +};
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling
2025-05-18 11:13 ` [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling Lothar Rubusch
@ 2025-05-19 11:48 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 11:48 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:15AM +0000, Lothar Rubusch wrote:
> Evaluate the devicetree property for an optional interrupt line, and
> configure the interrupt mapping accordingly. When no interrupt line
> is defined in the devicetree, keep the FIFO in bypass mode as before.
...
> +#define ADXL313_INT_NONE 0
Hmm... I would rather make it U8_MAX, but it's up to you.
> +#define ADXL313_INT1 1
> +#define ADXL313_INT2 2
...
> + /* FIFO_STREAM mode */
> + regval = int_line == ADXL313_INT2 ? 0xff : 0;
One space too many.
> + ret = regmap_write(data->regmap, ADXL313_REG_INT_MAP, regval);
Don't you want to use regmap_assign_bits() or something like this to have
the above ternary be included?
> + if (ret)
> + return ret;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v1 07/12] iio: accel: adxl313: add basic interrupt handling
2025-05-18 11:13 ` [PATCH v1 07/12] iio: accel: adxl313: add basic " Lothar Rubusch
@ 2025-05-19 11:59 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 11:59 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:16AM +0000, Lothar Rubusch wrote:
> Prepare the interrupt handler. Add register entries to evaluate the
> incoming interrupt. Add functions to clear status registers and reset the
> FIFO.
...
> +static int adxl313_set_fifo(struct adxl313_data *data)
> +{
> + unsigned int int_line;
> + int ret;
> +
> + ret = adxl313_set_measure_en(data, false);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(data->regmap, ADXL313_REG_INT_MAP, &int_line);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
> + FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK,
> + data->fifo_mode));
Better to have these two on one line. It makes it easier to read.
> + return adxl313_set_measure_en(data, true);
> +}
...
> +static int adxl313_fifo_transfer(struct adxl313_data *data, int samples)
> +{
> + size_t count;
> + int i;
> + int ret;
> + ret = 0;
Useless. See below.
> + count = sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS;
Perhaps use array_size() from overflow.h?
> + for (i = 0; i < samples; i++) {
> + ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE,
> + data->fifo_buf + (i * count / 2), count);
> + if (ret)
> + return ret;
> + }
> + return ret;
Just return 0 here.
> +}
...
> +static irqreturn_t adxl313_irq_handler(int irq, void *p)
> +{
> + struct iio_dev *indio_dev = p;
> + struct adxl313_data *data = iio_priv(indio_dev);
> + int int_stat;
> +
> + if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
> + return IRQ_NONE;
> +
> + if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
> + goto err;
> +
> + return IRQ_HANDLED;
> +err:
> + adxl313_fifo_reset(data);
> +
> + return IRQ_HANDLED;
Why do we need the label? Same can be achieved without it by moving the call
under the conditional.
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v1 09/12] iio: accel: adxl313: add activity sensing
2025-05-18 11:13 ` [PATCH v1 09/12] iio: accel: adxl313: add activity sensing Lothar Rubusch
@ 2025-05-19 12:15 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 12:15 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:18AM +0000, Lothar Rubusch wrote:
> 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.
...
> +static int adxl313_is_act_inact_en(struct adxl313_data *data,
> + enum adxl313_activity_type type,
> + bool *en)
> +{
> + unsigned int axis_ctrl;
> + unsigned int regval;
> + int ret;
> + *en = false;
Even in case of an error? The rule of thumb is to avoid assigning output when
we know that the error will be returned to the caller.
> + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl);
> + if (ret)
> + return ret;
> + if (type == ADXL313_ACTIVITY)
> + *en = FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl);
> +
> + if (*en) {
This doesn't need to re-write the value of *en. Just declare local boolean
temporary variable and use it and only assign it on success.
> + ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val);
> + if (ret)
> + return ret;
> +
> + *en = adxl313_act_int_reg[type] & regval;
> + }
> +
> + return 0;
> +}
...
> +static int adxl313_set_act_inact_en(struct adxl313_data *data,
> + enum adxl313_activity_type type,
> + bool cmd_en)
> +{
> + unsigned int axis_ctrl = 0;
> + unsigned int threshold;
> + bool en;
> + int ret;
> +
> + if (type == ADXL313_ACTIVITY)
> + axis_ctrl = ADXL313_ACT_XYZ_EN;
> +
> + ret = regmap_update_bits(data->regmap,
> + ADXL313_REG_ACT_INACT_CTL,
> + axis_ctrl,
> + cmd_en ? 0xff : 0x00);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type], &threshold);
> + if (ret)
> + return ret;
> + en = false;
Instead...
> + if (type == ADXL313_ACTIVITY)
> + en = cmd_en && threshold;
else
en = false;
> + return regmap_update_bits(data->regmap, ADXL313_REG_INT_ENABLE,
> + adxl313_act_int_reg[type],
> + en ? adxl313_act_int_reg[type] : 0);
> +}
...
> +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);
> + bool int_en;
Why? You return the int here... I would expect rather to see unsigned int...
> + int ret;
> +
> + switch (type) {
> + case IIO_EV_TYPE_MAG:
> + switch (dir) {
> + case IIO_EV_DIR_RISING:
> + ret = adxl313_is_act_inact_en(data,
> + ADXL313_ACTIVITY,
> + &int_en);
> + if (ret)
> + return ret;
> + return int_en;
...or even simply
return adx1313...(...);
> + default:
> + return -EINVAL;
> + }
> + 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 */
> +
> + switch (type) {
> + case IIO_EV_TYPE_MAG:
> + 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 = 1000000;
MICRO?
> + return IIO_VAL_FRACTIONAL;
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +
> + 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;
> +
> + switch (type) {
> + case IIO_EV_TYPE_MAG:
This can be collapsed to the conditional, making indentation better overall.
Same applies to the other parts of the code outside of this function.
> + switch (info) {
> + case IIO_EV_INFO_VALUE:
> + /* The scale factor is 15.625 mg/LSB */
> + regval = DIV_ROUND_CLOSEST(1000000 * val + val2, 15625);
MICRO?
> + 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;
> + }
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +}
...
> + ret = regmap_write(data->regmap, ADXL313_REG_ACT_INACT_CTL, 0);
0x00 ?
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52);
> + if (ret)
> + return ret;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing
2025-05-18 11:13 ` [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing Lothar Rubusch
@ 2025-05-19 12:17 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 12:17 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:19AM +0000, Lothar Rubusch 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.
...
> en = false;
You see, now it becomes even useless code.
> - if (type == ADXL313_ACTIVITY)
> + if (type == ADXL313_ACTIVITY) {
> en = cmd_en && threshold;
> + } else {
> + ret = regmap_read(data->regmap, ADXL313_REG_TIME_INACT, &inact_time_s);
> + if (ret)
> + return ret;
> +
> + en = cmd_en && threshold && inact_time_s;
> + }
...
> + 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 = 1000000;
MICRO ?
> + return IIO_VAL_FRACTIONAL;
> default:
> return -EINVAL;
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity
2025-05-18 11:13 ` [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity Lothar Rubusch
@ 2025-05-19 12:19 ` Andy Shevchenko
0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2025-05-19 12:19 UTC (permalink / raw)
To: Lothar Rubusch
Cc: jic23, dlechner, nuno.sa, corbet, lucas.p.stankus, lars,
Michael.Hennerich, linux-iio, linux-doc, linux-kernel
On Sun, May 18, 2025 at 11:13:20AM +0000, Lothar Rubusch wrote:
> Link activity and inactivity to indicate the internal power-saving state.
> Add auto-sleep to be linked to inactivity.
...
> + en = en && act_en && inact_en;
> +
> + return regmap_update_bits(data->regmap,
> + ADXL313_REG_POWER_CTL,
> + ADXL313_POWER_CTL_INACT_MSK,
> + en ? (ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK)
> + : 0);
Make it a logical split
en ?
(ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK) : 0);
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2025-05-19 12:19 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-18 11:13 [PATCH v1 00/12] iio: accel: adxl313: add power-save on activity/inactivity Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 01/12] iio: accel: adxl313: add debug register Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 02/12] iio: accel: adxl313: introduce channel scan_index Lothar Rubusch
2025-05-19 10:46 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 03/12] iio: accel: adxl313: configure scan type for buffer Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 04/12] iio: accel: adxl313: make use of regmap cache Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 05/12] iio: accel: adxl313: add function to enable measurement Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 06/12] iio: accel: adxl313: prepare interrupt handling Lothar Rubusch
2025-05-19 11:48 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 07/12] iio: accel: adxl313: add basic " Lothar Rubusch
2025-05-19 11:59 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 08/12] iio: accel: adxl313: add FIFO watermark Lothar Rubusch
2025-05-18 11:13 ` [PATCH v1 09/12] iio: accel: adxl313: add activity sensing Lothar Rubusch
2025-05-19 12:15 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 10/12] iio: accel: adxl313: add inactivity sensing Lothar Rubusch
2025-05-19 12:17 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 11/12] iio: accel: adxl313: implement power-save on inactivity Lothar Rubusch
2025-05-19 12:19 ` Andy Shevchenko
2025-05-18 11:13 ` [PATCH v1 12/12] docs: iio: add ADXL313 accelerometer Lothar Rubusch
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).