diff mbox series

[v1,10/12] iio: accel: adxl345: add freefall feature

Message ID 20250128120100.205523-11-l.rubusch@gmail.com (mailing list archive)
State New
Headers show
Series iio: accel: adxl345: add interrupt based sensor events | expand

Commit Message

Lothar Rubusch Jan. 28, 2025, noon UTC
Add the freefall detection of the sensor together with a threshold and
time parameter. Add sysfs handle to enable/disable the feature and
introduce another sysfs macro. This is the third sysfs macro for sysfs
handles of this sensor. The three sysfs macros allow to cover all
sensor features and parameters.

Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
 drivers/iio/accel/adxl345_core.c | 127 +++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)
diff mbox series

Patch

diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index ef0a12fd59be..62d75d28b6fc 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -170,6 +170,9 @@  struct adxl345_state {
 	u32 tap_window_us;
 	bool tap_suppressed;
 
+	u8 ff_value;
+	u32 ff_time_ms;
+
 	__le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN);
 };
 
@@ -187,6 +190,11 @@  static struct iio_event_spec adxl345_events[] = {
 		.dir = IIO_EV_DIR_DOUBLETAP,
 		.mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
 	},
+	{
+		/* free fall */
+		.type = IIO_EV_TYPE_MAG,
+		.dir = IIO_EV_DIR_FALLING,
+	},
 };
 
 #define ADXL345_CHANNEL(index, reg, axis) {					\
@@ -442,6 +450,61 @@  static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int,
 	return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us);
 }
 
+/* ff */
+
+static int adxl345_is_ff_en(struct adxl345_state *st, bool *en)
+{
+	int ret;
+	unsigned int regval;
+
+	ret = adxl345_read_interrupts(st, &regval);
+	if (ret)
+		return ret;
+
+	*en = FIELD_GET(ADXL345_INT_FREE_FALL, st->int_map) > 0;
+
+	return 0;
+}
+
+static int adxl345_set_ff_en(struct adxl345_state *st, bool en)
+{
+	bool ff_en = en && st->ff_value > 0 && st->ff_time_ms > 0;
+
+	adxl345_intmap_switch_bit(st, ff_en, ADXL345_INT_FREE_FALL);
+
+	return adxl345_write_interrupts(st);
+}
+
+static int adxl345_set_ff_value(struct adxl345_state *st, u8 val)
+{
+	st->ff_value = val;
+
+	return regmap_write(st->regmap, ADXL345_REG_THRESH_FF, val);
+}
+
+static int adxl345_set_ff_time(struct adxl345_state *st, u32 val_int,
+			       u32 val_fract_us)
+{
+	unsigned int regval;
+	int val_ms;
+
+	/*
+	 * max value is 255 * 5000 us = 1.275000 seconds
+	 *
+	 * Note: the scaling is similar to the scaling in the ADXL380
+	 */
+	if (1000000 * val_int + val_fract_us > 1275000)
+		return -EINVAL;
+
+	val_ms = val_int * 1000 + DIV_ROUND_UP(val_fract_us, 1000);
+	st->ff_time_ms = val_ms;
+
+	regval = DIV_ROUND_CLOSEST(val_ms, 5);
+
+	/* Values between 100ms and 350ms (0x14 to 0x46) are recommended. */
+	return regmap_write(st->regmap, ADXL345_REG_TIME_FF, min(regval, 0xff));
+}
+
 static int adxl345_read_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan,
 			    int *val, int *val2, long mask)
@@ -711,6 +774,49 @@  static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
 	}
 }
 
+#define ADXL345_generate_iio_dev_attr_INT(A, B, C)			\
+	static ssize_t in_accel_##A##_##B##_##C##_show(struct device *dev, \
+						       struct device_attribute *attr, \
+						       char *buf)	\
+	{								\
+		struct iio_dev *indio_dev = dev_to_iio_dev(dev);	\
+		struct adxl345_state *st = iio_priv(indio_dev);		\
+		int val;						\
+									\
+		val = st->B##_##C;					\
+									\
+		return iio_format_value(buf, IIO_VAL_INT, 1, &val);	\
+	}								\
+									\
+	static ssize_t in_accel_##A##_##B##_##C##_store(struct device *dev, \
+							struct device_attribute *attr, \
+							const char *buf, size_t len) \
+	{								\
+		struct iio_dev *indio_dev = dev_to_iio_dev(dev);	\
+		struct adxl345_state *st = iio_priv(indio_dev);		\
+		int val, ret;						\
+									\
+		ret = kstrtoint(buf, 0, &val);				\
+		if (ret)						\
+			return ret;					\
+									\
+		if (val < 0 || val > 255)				\
+			return -EINVAL;					\
+									\
+		ret = adxl345_set_measure_en(st, false);		\
+		if (ret)						\
+			return ret;					\
+									\
+		adxl345_set_##B##_##C(st, val);				\
+									\
+		ret = adxl345_set_measure_en(st, true);			\
+		if (ret)						\
+			return ret;					\
+									\
+		return len;						\
+	}								\
+	static IIO_DEVICE_ATTR_RW(in_accel_##A##_##B##_##C, 0)
+
 #define ADXL345_generate_iio_dev_attr_EN(A, B)				\
 	static ssize_t in_accel_##A##_##B##_en_show(struct device *dev, \
 						    struct device_attribute *attr, \
@@ -798,13 +904,20 @@  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(freefall, ff, value);
+
 ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_singletap, tap, duration, MICRO, us);
 ADXL345_generate_iio_dev_attr_FRACTIONAL(gesture_doubletap, tap, window, MICRO, us);
 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(freefall, ff);
 ADXL345_generate_iio_dev_attr_EN(gesture_doubletap, suppressed);
 
 static struct attribute *adxl345_event_attrs[] = {
+	&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,
 	&iio_dev_attr_in_accel_gesture_singletap_duration_us.dev_attr.attr,
 	&iio_dev_attr_in_accel_gesture_doubletap_suppressed_en.dev_attr.attr,
 	&iio_dev_attr_in_accel_gesture_doubletap_latent_us.dev_attr.attr,
@@ -1041,6 +1154,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;
+	}
+
 	return -ENOENT;
 }
 
@@ -1148,6 +1272,9 @@  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->ff_value = 8;			/*    8 [0x08]            */
+	st->ff_time_ms = 32;			/*   32 [0x20] -> 0.16    */
+
 	indio_dev->name = st->info->name;
 	indio_dev->info = &adxl345_info;
 	indio_dev->modes = INDIO_DIRECT_MODE;