@@ -121,6 +121,8 @@
#define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0)
#define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3)
+#define ADXL345_REG_ACT_AXIS_MSK GENMASK(6, 4)
+#define ADXL345_REG_ACT_ACDC_MSK BIT(7)
enum adxl345_axis {
ADXL345_Z_EN = BIT(0),
@@ -163,6 +165,10 @@ struct adxl345_state {
u8 watermark;
u8 fifo_mode;
+ u32 act_axis_ctrl;
+ bool act_ac;
+ u8 act_value;
+
u32 tap_axis_ctrl;
u8 tap_threshold;
u32 tap_duration_us;
@@ -177,6 +183,11 @@ struct adxl345_state {
};
static struct iio_event_spec adxl345_events[] = {
+ {
+ /* activity */
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ },
{
/* single tap */
.type = IIO_EV_TYPE_GESTURE,
@@ -276,6 +287,117 @@ static inline int adxl345_write_interrupts(struct adxl345_state *st)
return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map);
}
+/* act/inact */
+
+static int adxl345_write_act_axis(struct adxl345_state *st, bool en)
+{
+ int ret;
+
+ /*
+ * A setting of 0 selects dc-coupled operation, and a setting of 1
+ * enables ac-coupled operation. In dc-coupled operation, the current
+ * acceleration magnitude is compared directly with THRESH_ACT and
+ * THRESH_INACT to determine whether activity or inactivity is
+ * detected.
+ *
+ * In ac-coupled operation for activity detection, the acceleration
+ * value at the start of activity detection is taken as a reference
+ * value. New samples of acceleration are then compared to this
+ * reference value, and if the magnitude of the difference exceeds the
+ * THRESH_ACT value, the device triggers an activity interrupt.
+ *
+ * Similarly, in ac-coupled operation for inactivity detection, a
+ * reference value is used for comparison and is updated whenever the
+ * device exceeds the inactivity threshold. After the reference value
+ * is selected, the device compares the magnitude of the difference
+ * between the reference value and the current acceleration with
+ * THRESH_INACT. If the difference is less than the value in
+ * THRESH_INACT for the time in TIME_INACT, the device is considered
+ * inactive and the inactivity interrupt is triggered.
+ */
+ ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
+ ADXL345_REG_ACT_ACDC_MSK, st->act_ac);
+ if (ret)
+ return ret;
+
+ /*
+ * The ADXL345 allows for individually enabling/disabling axis for
+ * activity and inactivity detection, respectively. Here both axis are
+ * kept in sync, i.e. an axis will be generally enabled or disabled for
+ * both equally, activity and inactivity detection.
+ */
+ st->act_axis_ctrl = en
+ ? st->act_axis_ctrl | ADXL345_REG_ACT_AXIS_MSK
+ : st->act_axis_ctrl & ~ADXL345_REG_ACT_AXIS_MSK;
+
+ ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
+ ADXL345_REG_ACT_AXIS_MSK,
+ st->act_axis_ctrl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int adxl345_set_act_int(struct adxl345_state *st)
+{
+ bool args_valid;
+ bool axis_en;
+
+ axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
+ args_valid = axis_en && st->act_value > 0;
+ adxl345_intmap_switch_bit(st, args_valid, ADXL345_INT_ACTIVITY);
+
+ return adxl345_write_interrupts(st);
+}
+
+static int _adxl345_is_act_en(struct adxl345_state *st, bool *en)
+{
+ int ret;
+ unsigned int regval;
+
+ ret = adxl345_read_interrupts(st, ®val);
+ if (ret)
+ return ret;
+
+ *en = FIELD_GET(ADXL345_INT_ACTIVITY, regval) > 0;
+
+ return 0;
+}
+
+static int _adxl345_set_act_en(struct adxl345_state *st, bool en)
+{
+ int ret;
+
+ ret = adxl345_write_act_axis(st, en);
+ if (ret)
+ return ret;
+
+ return adxl345_set_act_int(st);
+}
+
+static int adxl345_is_act_en(struct adxl345_state *st, bool *en)
+{
+ return _adxl345_is_act_en(st, en);
+}
+
+static int adxl345_set_act_en(struct adxl345_state *st, bool en)
+{
+ return _adxl345_set_act_en(st, en);
+}
+
+static int _adxl345_set_act_value(struct adxl345_state *st, u8 val)
+{
+ st->act_value = val;
+
+ return regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, val);
+}
+
+static int adxl345_set_act_value(struct adxl345_state *st, u8 val)
+{
+ return _adxl345_set_act_value(st, val);
+}
+
/* tap */
static int adxl345_write_tap_axis(struct adxl345_state *st,
@@ -904,6 +1026,7 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
} \
static IIO_DEVICE_ATTR_RW(in_accel_##A##_##C##_##E, 0)
+ADXL345_generate_iio_dev_attr_INT(activity, act, value);
ADXL345_generate_iio_dev_attr_INT(freefall, ff, value);
ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_singletap, tap, duration, MICRO, us);
@@ -911,10 +1034,13 @@ ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, window, MICRO,
ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, latent, MICRO, us);
ADXL345_generate_iio_dev_attr_FRACTIONAL(freefall, ff, time, MILLI, ms);
+ADXL345_generate_iio_dev_attr_EN(activity, act);
ADXL345_generate_iio_dev_attr_EN(freefall, ff);
ADXL345_generate_iio_dev_attr_EN(gesture_doubletap, suppressed);
static struct attribute *adxl345_event_attrs[] = {
+ &iio_dev_attr_in_accel_activity_act_en.dev_attr.attr,
+ &iio_dev_attr_in_accel_activity_act_value.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_ff_en.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_ff_value.dev_attr.attr,
&iio_dev_attr_in_accel_freefall_time_ms.dev_attr.attr,
@@ -1087,20 +1213,25 @@ static int adxl345_get_status(struct adxl345_state *st, unsigned int *int_stat,
{
unsigned int regval;
bool check_tap_stat;
+ bool check_act_stat;
*act_tap_dir = IIO_NO_MOD;
check_tap_stat = FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, st->tap_axis_ctrl) > 0;
+ check_act_stat = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0;
- if (check_tap_stat) {
+ if (check_tap_stat || check_act_stat) {
/* ACT_TAP_STATUS should be read before clearing the interrupt */
if (regmap_read(st->regmap, ADXL345_REG_ACT_TAP_STATUS, ®val))
return -EINVAL;
- if (FIELD_GET(ADXL345_Z_EN, regval) > 0)
+ if ((FIELD_GET(ADXL345_Z_EN, regval >> 4)
+ | FIELD_GET(ADXL345_Z_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_Z;
- else if (FIELD_GET(ADXL345_Y_EN, regval) > 0)
+ else if ((FIELD_GET(ADXL345_Y_EN, regval >> 4)
+ | FIELD_GET(ADXL345_Y_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_Y;
- else if (FIELD_GET(ADXL345_X_EN, regval) > 0)
+ else if ((FIELD_GET(ADXL345_X_EN, regval >> 4)
+ | FIELD_GET(ADXL345_X_EN, regval)) > 0)
*act_tap_dir = IIO_MOD_X;
}
@@ -1154,6 +1285,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat,
return ret;
}
+ if (FIELD_GET(ADXL345_INT_ACTIVITY, int_stat)) {
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ act_tap_dir,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ ts);
+ if (ret)
+ return ret;
+ }
+
if (FIELD_GET(ADXL345_INT_FREE_FALL, int_stat)) {
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
@@ -1264,6 +1406,13 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
return -ENODEV;
st->fifo_delay = fifo_delay_default;
+ /*
+ * If the feature is enabled, scan all axis for activity and or
+ * inactivity, and set activity and inactivity to the same ac / dc
+ * setup.
+ */
+ st->act_axis_ctrl = ADXL345_REG_ACT_AXIS_MSK;
+ st->act_ac = 0;
st->int_map = 0x00; /* reset interrupts */
/* Init with reasonable values */
@@ -1272,6 +1421,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
st->tap_window_us = 20; /* 20 [0x14] -> .025 */
st->tap_latent_us = 20; /* 20 [0x14] -> .025 */
+ st->act_value = 6; /* 6 [0x06] */
st->ff_value = 8; /* 8 [0x08] */
st->ff_time_ms = 32; /* 32 [0x20] -> 0.16 */
Add the handling of activity events, also add sysfs entries to configure threshold values to trigger the event. Allow to push the event over to the iio channel. Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com> --- drivers/iio/accel/adxl345_core.c | 158 ++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 4 deletions(-)