Add handling for inactivity events, together with threshold and timeout to trigger the event. The event could be used for ODR and g range reduction for power saving using low power modes or sleep modes, not covered by this patch. Signed-off-by: Lothar Rubusch <l.rubusch@xxxxxxxxx> --- drivers/iio/accel/adxl345_core.c | 167 ++++++++++++++++++++++++++----- 1 file changed, 142 insertions(+), 25 deletions(-) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 94c3ad818ba5..b9f42c56e8f1 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -123,6 +123,8 @@ #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) +#define ADXL345_REG_INACT_AXIS_MSK GENMASK(2, 0) +#define ADXL345_REG_INACT_ACDC_MSK BIT(3) enum adxl345_axis { ADXL345_Z_EN = BIT(0), @@ -155,6 +157,32 @@ static const unsigned int adxl345_tap_time_reg[3] = { [ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR, }; +/* activity/inactivity */ +enum adxl345_activity_type { + ADXL345_ACTIVITY, + ADXL345_INACTIVITY +}; + +static const unsigned int adxl345_act_int_reg[2] = { + [ADXL345_ACTIVITY] = ADXL345_INT_ACTIVITY, + [ADXL345_INACTIVITY] = ADXL345_INT_INACTIVITY, +}; + +static const unsigned int adxl345_act_thresh_reg[2] = { + [ADXL345_ACTIVITY] = ADXL345_REG_THRESH_ACT, + [ADXL345_INACTIVITY] = ADXL345_REG_THRESH_INACT, +}; + +static const unsigned int adxl345_act_acdc_msk[2] = { + [ADXL345_ACTIVITY] = ADXL345_REG_ACT_ACDC_MSK, + [ADXL345_INACTIVITY] = ADXL345_REG_INACT_ACDC_MSK +}; + +static const unsigned int adxl345_act_axis_msk[2] = { + [ADXL345_ACTIVITY] = ADXL345_REG_ACT_AXIS_MSK, + [ADXL345_INACTIVITY] = ADXL345_REG_INACT_AXIS_MSK +}; + struct adxl345_state { const struct adxl345_chip_info *info; struct regmap *regmap; @@ -169,6 +197,11 @@ struct adxl345_state { bool act_ac; u8 act_value; + u32 inact_axis_ctrl; + bool inact_ac; + u8 inact_value; + u8 inact_time_s; + u32 tap_axis_ctrl; u8 tap_threshold; u32 tap_duration_us; @@ -188,6 +221,11 @@ static struct iio_event_spec adxl345_events[] = { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, }, + { + /* inactivity */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + }, { /* single tap */ .type = IIO_EV_TYPE_GESTURE, @@ -289,7 +327,8 @@ static inline int adxl345_write_interrupts(struct adxl345_state *st) /* act/inact */ -static int adxl345_write_act_axis(struct adxl345_state *st, bool en) +static int adxl345_write_act_axis(struct adxl345_state *st, + enum adxl345_activity_type type, bool en) { int ret; @@ -316,7 +355,9 @@ static int adxl345_write_act_axis(struct adxl345_state *st, bool en) * 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); + adxl345_act_acdc_msk[type], + (type == ADXL345_ACTIVITY + ? st->act_ac : st->inact_ac)); if (ret) return ret; @@ -326,32 +367,52 @@ static int adxl345_write_act_axis(struct adxl345_state *st, bool en) * 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; + if (type == ADXL345_ACTIVITY) { + 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_act_axis_msk[type], + st->act_axis_ctrl); + if (ret) + return ret; - ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL, - ADXL345_REG_ACT_AXIS_MSK, - st->act_axis_ctrl); - if (ret) - return ret; + } else { + st->inact_axis_ctrl = en + ? st->inact_axis_ctrl | ADXL345_REG_INACT_AXIS_MSK + : st->inact_axis_ctrl & ~ADXL345_REG_INACT_AXIS_MSK; + ret = regmap_update_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL, + adxl345_act_axis_msk[type], + st->inact_axis_ctrl); + if (ret) + return ret; + } return 0; } -static int adxl345_set_act_int(struct adxl345_state *st) +static int adxl345_set_act_int(struct adxl345_state *st, + enum adxl345_activity_type type) { 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); + if (type == ADXL345_ACTIVITY) { + axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0; + args_valid = axis_en && st->act_value > 0; + } else { + axis_en = FIELD_GET(ADXL345_REG_INACT_AXIS_MSK, st->inact_axis_ctrl) > 0; + args_valid = axis_en && st->inact_value > 0 && + st->inact_time_s > 0; + } + adxl345_intmap_switch_bit(st, args_valid, adxl345_act_int_reg[type]); return adxl345_write_interrupts(st); } -static int _adxl345_is_act_en(struct adxl345_state *st, bool *en) +static int _adxl345_is_act_en(struct adxl345_state *st, + enum adxl345_activity_type type, bool *en) { int ret; unsigned int regval; @@ -360,42 +421,76 @@ static int _adxl345_is_act_en(struct adxl345_state *st, bool *en) if (ret) return ret; - *en = FIELD_GET(ADXL345_INT_ACTIVITY, regval) > 0; + *en = (adxl345_act_int_reg[type] & regval) > 0; return 0; } -static int _adxl345_set_act_en(struct adxl345_state *st, bool en) +static int _adxl345_set_act_en(struct adxl345_state *st, + enum adxl345_activity_type type, bool en) { int ret; - ret = adxl345_write_act_axis(st, en); + ret = adxl345_write_act_axis(st, type, en); if (ret) return ret; - return adxl345_set_act_int(st); + return adxl345_set_act_int(st, type); } static int adxl345_is_act_en(struct adxl345_state *st, bool *en) { - return _adxl345_is_act_en(st, en); + return _adxl345_is_act_en(st, ADXL345_ACTIVITY, en); } static int adxl345_set_act_en(struct adxl345_state *st, bool en) { - return _adxl345_set_act_en(st, en); + return _adxl345_set_act_en(st, ADXL345_ACTIVITY, en); } -static int _adxl345_set_act_value(struct adxl345_state *st, u8 val) +static int adxl345_is_inact_en(struct adxl345_state *st, bool *en) { - st->act_value = val; + return _adxl345_is_act_en(st, ADXL345_INACTIVITY, en); +} - return regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, val); +static int adxl345_set_inact_en(struct adxl345_state *st, bool en) +{ + return _adxl345_set_act_en(st, ADXL345_INACTIVITY, en); +} + +static int _adxl345_set_act_value(struct adxl345_state *st, + enum adxl345_activity_type type, u8 val) +{ + switch (type) { + case ADXL345_ACTIVITY: + st->act_value = val; + break; + case ADXL345_INACTIVITY: + st->inact_value = val; + break; + default: + return -EINVAL; + } + + return regmap_write(st->regmap, adxl345_act_thresh_reg[type], val); } static int adxl345_set_act_value(struct adxl345_state *st, u8 val) { - return _adxl345_set_act_value(st, val); + return _adxl345_set_act_value(st, ADXL345_ACTIVITY, val); +} + +static int adxl345_set_inact_value(struct adxl345_state *st, u8 val) +{ + return _adxl345_set_act_value(st, ADXL345_INACTIVITY, val); +} + +static int adxl345_set_inact_time_s(struct adxl345_state *st, u32 val_s) +{ + st->inact_time_s = min(val_s, 0xff); + + return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, + st->inact_time_s); } /* tap */ @@ -1027,6 +1122,8 @@ 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(activity, inact, value); +ADXL345_generate_iio_dev_attr_INT(activity, inact, time_s); ADXL345_generate_iio_dev_attr_INT(freefall, ff, value); ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_singletap, tap, duration, MICRO, us); @@ -1035,12 +1132,16 @@ ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, latent, MICRO, 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(activity, inact); 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_activity_inact_en.dev_attr.attr, + &iio_dev_attr_in_accel_activity_inact_value.dev_attr.attr, + &iio_dev_attr_in_accel_activity_inact_time_s.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, @@ -1296,6 +1397,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat, return ret; } + if (FIELD_GET(ADXL345_INT_INACTIVITY, 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_THRESH, + IIO_EV_DIR_FALLING), + 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, @@ -1412,6 +1524,8 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, * setup. */ st->act_axis_ctrl = ADXL345_REG_ACT_AXIS_MSK; + st->inact_axis_ctrl = ADXL345_REG_INACT_AXIS_MSK; + st->inact_ac = 0; /* 0 [dc] */ st->act_ac = 0; st->int_map = 0x00; /* reset interrupts */ @@ -1422,6 +1536,9 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, st->tap_latent_us = 20; /* 20 [0x14] -> .025 */ st->act_value = 6; /* 6 [0x06] */ + st->inact_value = 4; /* 4 [0x04] */ + st->inact_time_s = 3; /* 3 [0x03] -> 3 */ + st->ff_value = 8; /* 8 [0x08] */ st->ff_time_ms = 32; /* 32 [0x20] -> 0.16 */ -- 2.39.5