Add the inactivity feature of the sensor. When activity and inactivity is enabled, a link bit will be set linking activity and inactivity handling. Additionally, the auto-sleep mode will be enabled. Due to the link bit the sensor is going to auto-sleep when inactivity was detected. Inactivity detection needs a threshold to be configured, and a time after which it will go into inactivity state if measurements under threshold. When a ODR is configured this time for inactivity is adjusted with a corresponding reasonable default value, in order to have higher frequencies and lower inactivity times, and lower sample frequency but give more time until inactivity. Both with reasonable upper and lower boundaries, since many of the sensor's features (e.g. auto-sleep) will need to operate beween 12.5 Hz and 400 Hz. This is a default setting when actively changing sample frequency, explicitly setting the time until inactivity will overwrite the default. Similarly, setting the g-range will provide a default value for the activity and inactivity thresholds. Both are implicit defaults, but equally can be overwritten to be explicitly configured. Signed-off-by: Lothar Rubusch <l.rubusch@xxxxxxxxx> --- drivers/iio/accel/adxl345_core.c | 140 ++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 16dea2a222d9..7de869fac799 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -36,6 +36,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_INACT_AXIS_MSK GENMASK(2, 0) +#define ADXL345_POWER_CTL_INACT_MSK (ADXL345_POWER_CTL_AUTO_SLEEP | ADXL345_POWER_CTL_LINK) enum adxl345_axis { ADXL345_Z_EN = BIT(0), @@ -71,18 +73,22 @@ static const unsigned int adxl345_tap_time_reg[3] = { /* 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_axis_msk[2] = { [ADXL345_ACTIVITY] = ADXL345_REG_ACT_AXIS_MSK, + [ADXL345_INACTIVITY] = ADXL345_REG_INACT_AXIS_MSK, }; enum adxl345_odr { @@ -174,6 +180,10 @@ struct adxl345_state { u32 act_axis_ctrl; u8 act_threshold; + u32 inact_axis_ctrl; + u8 inact_threshold; + u8 inact_time_s; + u32 tap_axis_ctrl; u8 tap_threshold; u32 tap_duration_us; @@ -195,6 +205,14 @@ static struct iio_event_spec adxl345_events[] = { .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), }, + { + /* inactivity */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, { /* single tap */ .type = IIO_EV_TYPE_GESTURE, @@ -301,6 +319,17 @@ static int adxl345_write_act_axis(struct adxl345_state *st, 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; } @@ -324,6 +353,7 @@ static int adxl345_set_act_inact_en(struct adxl345_state *st, enum adxl345_activity_type type, bool cmd_en) { bool axis_en, en = false; + unsigned long autosleep = 0; int ret; ret = adxl345_write_act_axis(st, type, cmd_en); @@ -333,12 +363,24 @@ static int adxl345_set_act_inact_en(struct adxl345_state *st, if (type == ADXL345_ACTIVITY) { axis_en = FIELD_GET(ADXL345_REG_ACT_AXIS_MSK, st->act_axis_ctrl) > 0; en = axis_en && st->act_threshold > 0; + } else { + axis_en = FIELD_GET(ADXL345_REG_INACT_AXIS_MSK, st->inact_axis_ctrl) > 0; + en = axis_en && st->inact_threshold > 0 && + st->inact_time_s > 0; } en ? __set_bit(ilog2(adxl345_act_int_reg[type]), (unsigned long *)&st->int_map) : __clear_bit(ilog2(adxl345_act_int_reg[type]), (unsigned long *)&st->int_map); - return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map); + ret = regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, st->int_map); + if (ret) + return ret; + + en ? __set_bit(ilog2(ADXL345_POWER_CTL_INACT_MSK), &autosleep) + : __clear_bit(ilog2(ADXL345_POWER_CTL_INACT_MSK), &autosleep); + + return regmap_update_bits(st->regmap, ADXL345_REG_POWER_CTL, + ADXL345_POWER_CTL_INACT_MSK, autosleep); } static int adxl345_set_act_inact_threshold(struct adxl345_state *st, @@ -352,6 +394,48 @@ static int adxl345_set_act_inact_threshold(struct adxl345_state *st, if (type == ADXL345_ACTIVITY) st->act_threshold = val; + else + st->inact_threshold = val; + + return 0; +} + +/** + * adxl345_set_inact_time_s - Configure inactivity time explicitly or by ODR. + * @st: The sensor state instance. + * @val_s: A desired time value, between 0 and 255. + * + * If val_s is 0, a default inactivity time will be computed. It should take + * power consumption into consideration. Thus it shall be shorter for higher + * frequencies and longer for lower frequencies. Hence, frequencies above 255 Hz + * shall default to 10 s and frequencies below 10 Hz shall result in 255 s to + * detect inactivity. + * + * The approach simply subtracts the pre-decimal figure of the configured + * sample frequency from 255 s to compute inactivity time [s]. Sub-Hz are thus + * ignored in this estimation. The recommended ODRs for various features + * (activity/inactivity, sleep modes, free fall, etc.) lie between 12.5 Hz and + * 400 Hz, thus higher or lower frequencies will result in the boundary + * defaults or need to be explicitly specified via val_s. + * + * Return: 0 or error value. + */ +static int adxl345_set_inact_time_s(struct adxl345_state *st, u32 val_s) +{ + unsigned int max_boundary = 255; + unsigned int min_boundary = 10; + unsigned int val = min(val_s, max_boundary); + int ret; + + if (val == 0) + val = (adxl345_odr_tbl[st->odr][0] > max_boundary) + ? min_boundary : max_boundary - adxl345_odr_tbl[st->odr][0]; + + ret = regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val); + if (ret) + return ret; + + st->inact_time_s = val; return 0; } @@ -641,6 +725,11 @@ static int adxl345_set_odr(struct adxl345_state *st, enum adxl345_odr odr) st->odr = odr; + /* update inactivity time by ODR */ + ret = adxl345_set_inact_time_s(st, 0); + if (ret) + return ret; + return 0; } @@ -672,6 +761,24 @@ static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range) if (ret) return ret; + st->act_threshold = st->act_threshold + * adxl345_range_factor_tbl[st->range] + / adxl345_range_factor_tbl[range]; + st->act_threshold = min(255, max(1, st->inact_threshold)); + + st->inact_threshold = st->inact_threshold + * adxl345_range_factor_tbl[st->range] + / adxl345_range_factor_tbl[range]; + st->inact_threshold = min(255, max(1, st->inact_threshold)); + + ret = adxl345_set_act_inact_threshold(st, ADXL345_ACTIVITY, st->act_threshold); + if (ret) + return ret; + + ret = adxl345_set_act_inact_threshold(st, ADXL345_INACTIVITY, st->inact_threshold); + if (ret) + return ret; + st->range = range; return 0; @@ -810,6 +917,11 @@ static int adxl345_read_event_config(struct iio_dev *indio_dev, if (ret) return ret; return int_en; + case IIO_EV_DIR_FALLING: + ret = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY, &int_en); + if (ret) + return ret; + return int_en; default: return -EINVAL; } @@ -866,6 +978,8 @@ static int adxl345_write_event_config(struct iio_dev *indio_dev, switch (dir) { case IIO_EV_DIR_RISING: return adxl345_set_act_inact_en(st, ADXL345_ACTIVITY, state); + case IIO_EV_DIR_FALLING: + return adxl345_set_act_inact_en(st, ADXL345_INACTIVITY, state); default: return -EINVAL; } @@ -913,9 +1027,15 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, const struct iio_ case IIO_EV_DIR_RISING: *val = st->act_threshold; return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = st->inact_threshold; + return IIO_VAL_INT; default: return -EINVAL; } + case IIO_EV_INFO_PERIOD: + *val = st->inact_time_s; + return IIO_VAL_INT; default: return -EINVAL; } @@ -982,10 +1102,16 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev, case IIO_EV_DIR_RISING: ret = adxl345_set_act_inact_threshold(st, ADXL345_ACTIVITY, val); break; + case IIO_EV_DIR_FALLING: + ret = adxl345_set_act_inact_threshold(st, ADXL345_INACTIVITY, val); + break; default: ret = -EINVAL; } break; + case IIO_EV_INFO_PERIOD: + ret = adxl345_set_inact_time_s(st, val); + break; default: ret = -EINVAL; } @@ -1365,6 +1491,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, @@ -1481,6 +1618,7 @@ 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->int_map = 0x00; /* reset interrupts */ /* Init with reasonable values */ -- 2.39.5