[PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s

Lothar Rubusch posted 7 patches 3 months, 2 weeks ago
There is a newer version of this series
[PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Lothar Rubusch 3 months, 2 weeks ago
Inactivity and free-fall events are essentially the same type of sensor
events. Therefore, inactivity detection (normally set for periods between 1
and 255 seconds) can be extended for shorter durations to support free-fall
detection.

For periods shorter than 1 second, the driver automatically configures the
threshold and duration using the free-fall register. For periods longer
than 1 second, it uses the inactivity threshold and duration using the
inactivity registers.

When using the free-fall register, the link bit is not set, which means
auto-sleep cannot be enabled if activity detection is also active.

Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
 drivers/iio/accel/adxl345_core.c | 158 +++++++++++++++++++++----------
 1 file changed, 109 insertions(+), 49 deletions(-)

diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index c5a3dac5f938..f1f92635bc21 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -40,12 +40,15 @@
 #define ADXL345_REG_INACT_AXIS_MSK	GENMASK(2, 0)
 #define ADXL345_REG_ACT_ACDC_MSK	BIT(7)
 #define ADXL345_REG_INACT_ACDC_MSK	BIT(3)
+#define ADXL345_REG_NO_ACDC_MSK		0x00
 #define ADXL345_POWER_CTL_INACT_MSK	(ADXL345_POWER_CTL_AUTO_SLEEP | ADXL345_POWER_CTL_LINK)
 
 #define ADXL345_TAP_Z_EN		BIT(0)
 #define ADXL345_TAP_Y_EN		BIT(1)
 #define ADXL345_TAP_X_EN		BIT(2)
 
+#define ADXL345_ACT_INACT_NO_AXIS_EN	0x00
+
 #define ADXL345_INACT_Z_EN		BIT(0)
 #define ADXL345_INACT_Y_EN		BIT(1)
 #define ADXL345_INACT_X_EN		BIT(2)
@@ -86,6 +89,7 @@ enum adxl345_activity_type {
 	ADXL345_INACTIVITY,
 	ADXL345_ACTIVITY_AC,
 	ADXL345_INACTIVITY_AC,
+	ADXL345_INACTIVITY_FF,
 };
 
 static const unsigned int adxl345_act_int_reg[] = {
@@ -93,6 +97,7 @@ static const unsigned int adxl345_act_int_reg[] = {
 	[ADXL345_INACTIVITY] = ADXL345_INT_INACTIVITY,
 	[ADXL345_ACTIVITY_AC] = ADXL345_INT_ACTIVITY,
 	[ADXL345_INACTIVITY_AC] = ADXL345_INT_INACTIVITY,
+	[ADXL345_INACTIVITY_FF] = ADXL345_INT_FREE_FALL,
 };
 
 static const unsigned int adxl345_act_thresh_reg[] = {
@@ -100,6 +105,7 @@ static const unsigned int adxl345_act_thresh_reg[] = {
 	[ADXL345_INACTIVITY] = ADXL345_REG_THRESH_INACT,
 	[ADXL345_ACTIVITY_AC] = ADXL345_REG_THRESH_ACT,
 	[ADXL345_INACTIVITY_AC] = ADXL345_REG_THRESH_INACT,
+	[ADXL345_INACTIVITY_FF] = ADXL345_REG_THRESH_FF,
 };
 
 static const unsigned int adxl345_act_acdc_msk[] = {
@@ -107,6 +113,7 @@ static const unsigned int adxl345_act_acdc_msk[] = {
 	[ADXL345_INACTIVITY] = ADXL345_REG_INACT_ACDC_MSK,
 	[ADXL345_ACTIVITY_AC] = ADXL345_REG_ACT_ACDC_MSK,
 	[ADXL345_INACTIVITY_AC] = ADXL345_REG_INACT_ACDC_MSK,
+	[ADXL345_INACTIVITY_FF] = ADXL345_REG_NO_ACDC_MSK,
 };
 
 enum adxl345_odr {
@@ -189,6 +196,9 @@ struct adxl345_state {
 	u8 watermark;
 	u8 fifo_mode;
 
+	u8 inact_threshold;
+	u32 inact_time_ms;
+
 	u32 tap_duration_us;
 	u32 tap_latent_us;
 	u32 tap_window_us;
@@ -333,10 +343,29 @@ static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
 
 /* activity / inactivity */
 
+static int adxl345_set_inact_threshold(struct adxl345_state *st,
+				       unsigned int threshold)
+{
+	int ret;
+
+	st->inact_threshold = min(U8_MAX, threshold);
+
+	ret = regmap_write(st->regmap,
+			   adxl345_act_thresh_reg[ADXL345_INACTIVITY],
+			   st->inact_threshold);
+	if (ret)
+		return ret;
+
+	return regmap_write(st->regmap,
+			    adxl345_act_thresh_reg[ADXL345_INACTIVITY_FF],
+			    st->inact_threshold);
+}
+
 /**
  * adxl345_set_inact_time - Configure inactivity time explicitly or by ODR.
  * @st: The sensor state instance.
- * @val_s: A desired time value, between 0 and 255.
+ * @val_int: The inactivity time, integer part.
+ * @val_fract: The inactivity time, fractional part when val_int is 0.
  *
  * Inactivity time can be configured between 1 and 255 seconds. If a user sets
  * val_s to 0, a default inactivity time is calculated automatically (since 0 is
@@ -357,16 +386,18 @@ static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
  *
  * Return: 0 or error value.
  */
-static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
+static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
+				  u32 val_fract)
 {
 	int max_boundary = U8_MAX;
 	int min_boundary = 10;
-	unsigned int val = min(val_s, U8_MAX);
+	unsigned int val;
 	enum adxl345_odr odr;
 	unsigned int regval;
 	int ret;
 
-	if (val == 0) {
+	if (val_int == 0 && val_fract == 0) {
+		/* Generated inactivity time based on ODR */
 		ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
 		if (ret)
 			return ret;
@@ -374,9 +405,31 @@ static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
 		odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
 		val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
 			    min_boundary, max_boundary);
+		st->inact_time_ms = MILLI * val;
+
+		/* Inactivity time in s */
+		return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
+	} else if (val_int == 0 && val_fract > 0) {
+		/* time < 1s, free-fall */
+
+		/*
+		 * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
+		 *
+		 * Recommended values between 100ms and 350ms (0x14 to 0x46)
+		 */
+		st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
+
+		return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
+				    DIV_ROUND_CLOSEST(val_fract, 5));
+	} else if (val_int > 0) {
+		/* Time >= 1s, inactivity */
+		st->inact_time_ms = MILLI * val_int;
+
+		return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
 	}
 
-	return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
+	/* Do not support negative or wrong input. */
+	return -EINVAL;
 }
 
 /**
@@ -399,6 +452,9 @@ static int adxl345_is_act_inact_ac(struct adxl345_state *st,
 	bool coupling;
 	int ret;
 
+	if (type == ADXL345_INACTIVITY_FF)
+		return true;
+
 	ret = regmap_read(st->regmap, ADXL345_REG_ACT_INACT_CTRL, &regval);
 	if (ret)
 		return ret;
@@ -511,6 +567,9 @@ static int adxl345_is_act_inact_en(struct adxl345_state *st,
 		if (!en)
 			return false;
 		break;
+	case ADXL345_INACTIVITY_FF:
+		en = true;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -542,15 +601,20 @@ static int adxl345_set_act_inact_linkbit(struct adxl345_state *st,
 	if (act_ac_en < 0)
 		return act_ac_en;
 
-	inact_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY);
-	if (inact_en < 0)
-		return inact_en;
+	if (type == ADXL345_INACTIVITY_FF) {
+		inact_en = false;
+	} else {
+		inact_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY);
+		if (inact_en < 0)
+			return inact_en;
 
-	inact_ac_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY_AC);
-	if (inact_ac_en < 0)
-		return inact_ac_en;
+		inact_ac_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY_AC);
+		if (inact_ac_en < 0)
+			return inact_ac_en;
+
+		inact_en = inact_en || inact_ac_en;
+	}
 
-	inact_en = inact_en || inact_ac_en;
 	act_en = act_en || act_ac_en;
 
 	return regmap_assign_bits(st->regmap, ADXL345_REG_POWER_CTL,
@@ -569,11 +633,15 @@ static int adxl345_set_act_inact_en(struct adxl345_state *st,
 
 	if (cmd_en) {
 		/* When turning on, check if threshold is valid */
-		ret = regmap_read(st->regmap,
-				  adxl345_act_thresh_reg[type],
-				  &threshold);
-		if (ret)
-			return ret;
+		if (type == ADXL345_ACTIVITY || type == ADXL345_ACTIVITY_AC) {
+			ret = regmap_read(st->regmap,
+					  adxl345_act_thresh_reg[type],
+					  &threshold);
+			if (ret)
+				return ret;
+		} else {
+			threshold = st->inact_threshold;
+		}
 
 		if (!threshold) /* Just ignore the command if threshold is 0 */
 			return 0;
@@ -618,6 +686,9 @@ static int adxl345_set_act_inact_en(struct adxl345_state *st,
 		axis_ctrl = ADXL345_INACT_X_EN | ADXL345_INACT_Y_EN |
 				ADXL345_INACT_Z_EN;
 		break;
+	case ADXL345_INACTIVITY_FF:
+		axis_ctrl = ADXL345_ACT_INACT_NO_AXIS_EN;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -884,7 +955,7 @@ static int adxl345_set_odr(struct adxl345_state *st, enum adxl345_odr odr)
 		return ret;
 
 	/* update inactivity time by ODR */
-	return adxl345_set_inact_time(st, 0);
+	return adxl345_set_inact_time(st, 0, 0);
 }
 
 static int adxl345_find_range(struct adxl345_state *st, int val, int val2,
@@ -921,12 +992,6 @@ static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range)
 	if (ret)
 		return ret;
 
-	ret = regmap_read(st->regmap,
-			  adxl345_act_thresh_reg[ADXL345_INACTIVITY],
-			  &inact_threshold);
-	if (ret)
-		return ret;
-
 	ret = regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT,
 				 ADXL345_DATA_FORMAT_RANGE,
 				 FIELD_PREP(ADXL345_DATA_FORMAT_RANGE, range));
@@ -937,6 +1002,7 @@ static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range)
 		/ adxl345_range_factor_tbl[range];
 	act_threshold = min(U8_MAX, max(1, act_threshold));
 
+	inact_threshold = st->inact_threshold;
 	inact_threshold = inact_threshold * adxl345_range_factor_tbl[range_old]
 		/ adxl345_range_factor_tbl[range];
 	inact_threshold = min(U8_MAX, max(1, inact_threshold));
@@ -946,8 +1012,7 @@ static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range)
 	if (ret)
 		return ret;
 
-	return regmap_write(st->regmap, adxl345_act_thresh_reg[ADXL345_INACTIVITY],
-			   inact_threshold);
+	return adxl345_set_inact_threshold(st, inact_threshold);
 }
 
 static int adxl345_read_avail(struct iio_dev *indio_dev,
@@ -1192,7 +1257,6 @@ static int adxl345_read_mag_value(struct adxl345_state *st,
 				  int *val, int *val2)
 {
 	unsigned int threshold;
-	unsigned int period;
 	int ret;
 
 	switch (info) {
@@ -1208,25 +1272,16 @@ static int adxl345_read_mag_value(struct adxl345_state *st,
 			*val2 = MICRO;
 			return IIO_VAL_FRACTIONAL;
 		case IIO_EV_DIR_FALLING:
-			ret = regmap_read(st->regmap,
-					  adxl345_act_thresh_reg[type_inact],
-					  &threshold);
-			if (ret)
-				return ret;
-			*val = 62500 * threshold;
+			*val = 62500 * st->inact_threshold;
 			*val2 = MICRO;
 			return IIO_VAL_FRACTIONAL;
 		default:
 			return -EINVAL;
 		}
 	case IIO_EV_INFO_PERIOD:
-		ret = regmap_read(st->regmap,
-				  ADXL345_REG_TIME_INACT,
-				  &period);
-		if (ret)
-			return ret;
-		*val = period;
-		return IIO_VAL_INT;
+		*val = st->inact_time_ms;
+		*val2 = MILLI;
+		return IIO_VAL_FRACTIONAL;
 	default:
 		return -EINVAL;
 	}
@@ -1249,14 +1304,12 @@ static int adxl345_write_mag_value(struct adxl345_state *st,
 					    adxl345_act_thresh_reg[type_act],
 					    val);
 		case IIO_EV_DIR_FALLING:
-			return regmap_write(st->regmap,
-					    adxl345_act_thresh_reg[type_inact],
-					    val);
+			return adxl345_set_inact_threshold(st, val);
 		default:
 			return -EINVAL;
 		}
 	case IIO_EV_INFO_PERIOD:
-		return adxl345_set_inact_time(st, val);
+		return adxl345_set_inact_time(st, val, val2);
 	default:
 		return -EINVAL;
 	}
@@ -1669,6 +1722,17 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat,
 			return ret;
 	}
 
+	if (FIELD_GET(ADXL345_INT_FREE_FALL, 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(ADXL345_INT_WATERMARK, int_stat)) {
 		samples = adxl345_get_samples(st);
 		if (samples < 0)
@@ -1907,15 +1971,11 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
 		if (ret)
 			return ret;
 
-		ret = regmap_write(st->regmap, ADXL345_REG_TIME_INACT, 0x37);
-		if (ret)
-			return ret;
-
 		ret = regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, 6);
 		if (ret)
 			return ret;
 
-		ret = regmap_write(st->regmap, ADXL345_REG_THRESH_INACT, 4);
+		ret = adxl345_set_inact_threshold(st, 4);
 		if (ret)
 			return ret;
 
-- 
2.39.5
Re: [PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Andy Shevchenko 3 months, 2 weeks ago
On Sun, Jun 22, 2025 at 03:50:09PM +0000, Lothar Rubusch wrote:
> Inactivity and free-fall events are essentially the same type of sensor
> events. Therefore, inactivity detection (normally set for periods between 1
> and 255 seconds) can be extended for shorter durations to support free-fall
> detection.
> 
> For periods shorter than 1 second, the driver automatically configures the
> threshold and duration using the free-fall register. For periods longer
> than 1 second, it uses the inactivity threshold and duration using the
> inactivity registers.
> 
> When using the free-fall register, the link bit is not set, which means
> auto-sleep cannot be enabled if activity detection is also active.

...

> -static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
> +static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
> +				  u32 val_fract)
>  {
>  	int max_boundary = U8_MAX;
>  	int min_boundary = 10;
> -	unsigned int val = min(val_s, U8_MAX);
> +	unsigned int val;

You see, I even suggested splitting this assignment to begin with.
The change will be clearer with that done.

>  	enum adxl345_odr odr;
>  	unsigned int regval;
>  	int ret;
>  
> -	if (val == 0) {
> +	if (val_int == 0 && val_fract == 0) {
> +		/* Generated inactivity time based on ODR */
>  		ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
>  		if (ret)
>  			return ret;

>  		odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
>  		val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
>  			    min_boundary, max_boundary);
> +		st->inact_time_ms = MILLI * val;
> +
> +		/* Inactivity time in s */
> +		return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> +	} else if (val_int == 0 && val_fract > 0) {

val_fract check is not needed here.

> +		/* time < 1s, free-fall */
> +
> +		/*
> +		 * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
> +		 *
> +		 * Recommended values between 100ms and 350ms (0x14 to 0x46)
> +		 */
> +		st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
> +
> +		return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
> +				    DIV_ROUND_CLOSEST(val_fract, 5));
> +	} else if (val_int > 0) {

if now is redundant here, right?

> +		/* Time >= 1s, inactivity */
> +		st->inact_time_ms = MILLI * val_int;
> +
> +		return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
>  	}
>  
> -	return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> +	/* Do not support negative or wrong input. */
> +	return -EINVAL;
>  }

-- 
With Best Regards,
Andy Shevchenko
Re: [PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Lothar Rubusch 3 months, 2 weeks ago
Hi Andy,

This is a tricky one, I'll give some examples why (I think) the code
is needed as is.


On Mon, Jun 23, 2025 at 12:17 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Sun, Jun 22, 2025 at 03:50:09PM +0000, Lothar Rubusch wrote:
> > Inactivity and free-fall events are essentially the same type of sensor
> > events. Therefore, inactivity detection (normally set for periods between 1
> > and 255 seconds) can be extended for shorter durations to support free-fall
> > detection.
> >
> > For periods shorter than 1 second, the driver automatically configures the
> > threshold and duration using the free-fall register. For periods longer
> > than 1 second, it uses the inactivity threshold and duration using the
> > inactivity registers.
> >
> > When using the free-fall register, the link bit is not set, which means
> > auto-sleep cannot be enabled if activity detection is also active.
>
> ...
>
> > -static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
> > +static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
> > +                               u32 val_fract)
> >  {
> >       int max_boundary = U8_MAX;
> >       int min_boundary = 10;
> > -     unsigned int val = min(val_s, U8_MAX);
> > +     unsigned int val;
>
> You see, I even suggested splitting this assignment to begin with.
> The change will be clearer with that done.
>
> >       enum adxl345_odr odr;
> >       unsigned int regval;
> >       int ret;
> >
> > -     if (val == 0) {
> > +     if (val_int == 0 && val_fract == 0) {

The case for 0sec, 0.0 or setting "0" and fract will consequently be
"0". 0 is an invalid input for this period and sensor, so it will
default to an optimized period based on given ODR.

> > +             /* Generated inactivity time based on ODR */
> >               ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
> >               if (ret)
> >                       return ret;
>
> >               odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
> >               val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
> >                           min_boundary, max_boundary);
> > +             st->inact_time_ms = MILLI * val;
> > +
> > +             /* Inactivity time in s */
> > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > +     } else if (val_int == 0 && val_fract > 0) {
>
> val_fract check is not needed here.
>

Case for e.g. 0.123, numbers under 1s. This goes into the free-fall register.

> > +             /* time < 1s, free-fall */
> > +
> > +             /*
> > +              * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
> > +              *
> > +              * Recommended values between 100ms and 350ms (0x14 to 0x46)
> > +              */
> > +             st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
> > +
> > +             return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
> > +                                 DIV_ROUND_CLOSEST(val_fract, 5));
> > +     } else if (val_int > 0) {
>
> if now is redundant here, right?
>

So, this will be 1s through 255s. Periods above 1sec. This goes into
the inactivity register.

> > +             /* Time >= 1s, inactivity */
> > +             st->inact_time_ms = MILLI * val_int;
> > +
> > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
> >       }
> >
> > -     return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > +     /* Do not support negative or wrong input. */
> > +     return -EINVAL;
> >  }
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
Re: [PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Andy Shevchenko 3 months, 2 weeks ago
On Mon, Jun 23, 2025 at 11:21:01PM +0200, Lothar Rubusch wrote:
> On Mon, Jun 23, 2025 at 12:17 PM Andy Shevchenko
> <andriy.shevchenko@intel.com> wrote:
> > On Sun, Jun 22, 2025 at 03:50:09PM +0000, Lothar Rubusch wrote:

...

> > > -static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
> > > +static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
> > > +                               u32 val_fract)
> > >  {
> > >       int max_boundary = U8_MAX;
> > >       int min_boundary = 10;
> > > -     unsigned int val = min(val_s, U8_MAX);
> > > +     unsigned int val;
> >
> > You see, I even suggested splitting this assignment to begin with.
> > The change will be clearer with that done.
> >
> > >       enum adxl345_odr odr;
> > >       unsigned int regval;
> > >       int ret;
> > >
> > > -     if (val == 0) {
> > > +     if (val_int == 0 && val_fract == 0) {
> 
> The case for 0sec, 0.0 or setting "0" and fract will consequently be
> "0". 0 is an invalid input for this period and sensor, so it will
> default to an optimized period based on given ODR.
> 
> > > +             /* Generated inactivity time based on ODR */
> > >               ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
> > >               if (ret)
> > >                       return ret;
> >
> > >               odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
> > >               val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
> > >                           min_boundary, max_boundary);
> > > +             st->inact_time_ms = MILLI * val;
> > > +
> > > +             /* Inactivity time in s */
> > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > > +     } else if (val_int == 0 && val_fract > 0) {
> >
> > val_fract check is not needed here.
> 
> Case for e.g. 0.123, numbers under 1s. This goes into the free-fall register.

0.0 is already checked above, and since the val_fract is unsigned this is check
is redundant.

> > > +             /* time < 1s, free-fall */
> > > +
> > > +             /*
> > > +              * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
> > > +              *
> > > +              * Recommended values between 100ms and 350ms (0x14 to 0x46)
> > > +              */
> > > +             st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
> > > +
> > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
> > > +                                 DIV_ROUND_CLOSEST(val_fract, 5));
> > > +     } else if (val_int > 0) {
> >
> > if now is redundant here, right?
> 
> So, this will be 1s through 255s. Periods above 1sec. This goes into
> the inactivity register.

See above,

> > > +             /* Time >= 1s, inactivity */
> > > +             st->inact_time_ms = MILLI * val_int;
> > > +
> > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
> > >       }
> > >
> > > -     return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > > +     /* Do not support negative or wrong input. */
> > > +     return -EINVAL;
> > >  }

-- 
With Best Regards,
Andy Shevchenko


Re: [PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Lothar Rubusch 3 months, 2 weeks ago
On Tue, Jun 24, 2025 at 9:38 AM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Mon, Jun 23, 2025 at 11:21:01PM +0200, Lothar Rubusch wrote:
> > On Mon, Jun 23, 2025 at 12:17 PM Andy Shevchenko
> > <andriy.shevchenko@intel.com> wrote:
> > > On Sun, Jun 22, 2025 at 03:50:09PM +0000, Lothar Rubusch wrote:
>
> ...
>
> > > > -static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_s)
> > > > +static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
> > > > +                               u32 val_fract)
> > > >  {
> > > >       int max_boundary = U8_MAX;
> > > >       int min_boundary = 10;
> > > > -     unsigned int val = min(val_s, U8_MAX);
> > > > +     unsigned int val;
> > >
> > > You see, I even suggested splitting this assignment to begin with.
> > > The change will be clearer with that done.
> > >
> > > >       enum adxl345_odr odr;
> > > >       unsigned int regval;
> > > >       int ret;
> > > >
> > > > -     if (val == 0) {
> > > > +     if (val_int == 0 && val_fract == 0) {
> >
> > The case for 0sec, 0.0 or setting "0" and fract will consequently be
> > "0". 0 is an invalid input for this period and sensor, so it will
> > default to an optimized period based on given ODR.
> >
> > > > +             /* Generated inactivity time based on ODR */
> > > >               ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
> > > >               if (ret)
> > > >                       return ret;
> > >
> > > >               odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
> > > >               val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
> > > >                           min_boundary, max_boundary);
> > > > +             st->inact_time_ms = MILLI * val;
> > > > +
> > > > +             /* Inactivity time in s */
> > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > > > +     } else if (val_int == 0 && val_fract > 0) {
> > >
> > > val_fract check is not needed here.
> >
> > Case for e.g. 0.123, numbers under 1s. This goes into the free-fall register.
>
> 0.0 is already checked above, and since the val_fract is unsigned this is check
> is redundant.
>
> > > > +             /* time < 1s, free-fall */
> > > > +
> > > > +             /*
> > > > +              * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
> > > > +              *
> > > > +              * Recommended values between 100ms and 350ms (0x14 to 0x46)
> > > > +              */
> > > > +             st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
> > > > +
> > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
> > > > +                                 DIV_ROUND_CLOSEST(val_fract, 5));
> > > > +     } else if (val_int > 0) {
> > >
> > > if now is redundant here, right?
> >
> > So, this will be 1s through 255s. Periods above 1sec. This goes into
> > the inactivity register.
>
> See above,
>

I agree, that checking for val_fract is actually done as a sub case of
val_int, and only if val_int was 0. So, would the following make it
clearer?

if (val_int  == 0) {
    if (val_fract == 0) {
        // 0 provided, default values
    } else {
        // >0s, e.g. 0.123s, use free-fall register
} else {
    // 1s - 255s, use inactivity register
}

Actually - I did not touch that - I saw some places where I'm already
using nested if/else in the third level. I guess, by the style advice
according to switch/case, this also applies to if/else, right?

If yes, when the according parts go into another round, I might give
it a try to separate as well using helper functions.

Best,
L

> > > > +             /* Time >= 1s, inactivity */
> > > > +             st->inact_time_ms = MILLI * val_int;
> > > > +
> > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
> > > >       }
> > > >
> > > > -     return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > > > +     /* Do not support negative or wrong input. */
> > > > +     return -EINVAL;
> > > >  }
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
Re: [PATCH v10 6/7] iio: accel: adxl345: extend inactivity time for less than 1s
Posted by Andy Shevchenko 3 months, 2 weeks ago
On Tue, Jun 24, 2025 at 10:18:35AM +0200, Lothar Rubusch wrote:
> On Tue, Jun 24, 2025 at 9:38 AM Andy Shevchenko
> <andriy.shevchenko@intel.com> wrote:
> > On Mon, Jun 23, 2025 at 11:21:01PM +0200, Lothar Rubusch wrote:
> > > On Mon, Jun 23, 2025 at 12:17 PM Andy Shevchenko
> > > <andriy.shevchenko@intel.com> wrote:
> > > > On Sun, Jun 22, 2025 at 03:50:09PM +0000, Lothar Rubusch wrote:

...

> > > > > -     if (val == 0) {
> > > > > +     if (val_int == 0 && val_fract == 0) {
> > >
> > > The case for 0sec, 0.0 or setting "0" and fract will consequently be
> > > "0". 0 is an invalid input for this period and sensor, so it will
> > > default to an optimized period based on given ODR.
> > >
> > > > > +             /* Generated inactivity time based on ODR */
> > > > >               ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
> > > > >               if (ret)
> > > > >                       return ret;
> > > >
> > > > >               odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
> > > > >               val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
> > > > >                           min_boundary, max_boundary);
> > > > > +             st->inact_time_ms = MILLI * val;
> > > > > +
> > > > > +             /* Inactivity time in s */
> > > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
> > > > > +     } else if (val_int == 0 && val_fract > 0) {
> > > >
> > > > val_fract check is not needed here.
> > >
> > > Case for e.g. 0.123, numbers under 1s. This goes into the free-fall register.
> >
> > 0.0 is already checked above, and since the val_fract is unsigned this is check
> > is redundant.
> >
> > > > > +             /* time < 1s, free-fall */
> > > > > +
> > > > > +             /*
> > > > > +              * Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
> > > > > +              *
> > > > > +              * Recommended values between 100ms and 350ms (0x14 to 0x46)
> > > > > +              */
> > > > > +             st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
> > > > > +
> > > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
> > > > > +                                 DIV_ROUND_CLOSEST(val_fract, 5));
> > > > > +     } else if (val_int > 0) {
> > > >
> > > > if now is redundant here, right?
> > >
> > > So, this will be 1s through 255s. Periods above 1sec. This goes into
> > > the inactivity register.
> >
> > See above,
> >
> 
> I agree, that checking for val_fract is actually done as a sub case of
> val_int, and only if val_int was 0. So, would the following make it
> clearer?
> 
> if (val_int  == 0) {
>     if (val_fract == 0) {
>         // 0 provided, default values
>     } else {
>         // >0s, e.g. 0.123s, use free-fall register
> } else {
>     // 1s - 255s, use inactivity register
> }
> 
> Actually - I did not touch that - I saw some places where I'm already
> using nested if/else in the third level. I guess, by the style advice
> according to switch/case, this also applies to if/else, right?
> 
> If yes, when the according parts go into another round, I might give
> it a try to separate as well using helper functions.

You can think through the patches. It might make sense to consider as well this

helper_1()
{
	// for default
}

helper_2()
{
	// for free-fall
}

helper_3()
{
	// for inactive
}

	...
	if ()
		helper_1();
	else if ()
		helper_2();
	else
		helper_3();


> > > > > +             /* Time >= 1s, inactivity */
> > > > > +             st->inact_time_ms = MILLI * val_int;
> > > > > +
> > > > > +             return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
> > > > >       }

-- 
With Best Regards,
Andy Shevchenko