linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/3] iio: accel: adxl372: add peak mode
@ 2020-08-03 17:22 alexandru.tachici
  2020-08-03 17:22 ` [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO " alexandru.tachici
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: alexandru.tachici @ 2020-08-03 17:22 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: jic23

From: Alexandru Tachici <alexandru.tachici@analog.com>

This series adds the possibility to configure
the device, from sysfs, to work in peak mode. This enables
adxl372 to capture only over threshold accelerations.

1. Create sysfs files for falling_period/rising_period
and thresh_falling_value/thresh_rising_value in events/ dir.
Set INT1 reg for activity/inactivity and push
event code in events fifo on irq.

2. Document use of iio events sysfs files.

Alexandru Tachici (2):
  iio: accel: adxl372: add event interface
  docs: iio: Add adxl372 documentation

1. Device FIFO can now be set in peak mode and only over the
threshold accelerations will be stored. Driver sets adxl372
FIFO in peak mode when the peak iio:trigger is selected.

Stefan Popa (1):
  iio: accel: adxl372: Add support for FIFO peak mode

  Changelog v3 -> v4:
  - add mutex for threshold write so that writing the two
  registers becomes atomic
  - added a trigger for peak mode, when selected in the trigger
  user interface, adxl372 will start recording only peak acceleration
  data in the fifo
  - added a adxl372.rst doc file explaining the use of iio events sysfs

 Documentation/iio/adxl372.rst |  46 +++++
 Documentation/iio/index.rst   |   1 +
 drivers/iio/accel/adxl372.c   | 328 +++++++++++++++++++++++++++++++++-
 3 files changed, 367 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/iio/adxl372.rst

-- 
2.20.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO peak mode
  2020-08-03 17:22 [PATCH v4 0/3] iio: accel: adxl372: add peak mode alexandru.tachici
@ 2020-08-03 17:22 ` alexandru.tachici
  2020-08-09 13:11   ` Jonathan Cameron
  2020-08-03 17:22 ` [PATCH v4 2/3] iio: accel: adxl372: add event interface alexandru.tachici
  2020-08-03 17:22 ` [PATCH v4 3/3] docs: iio: Add adxl372 documentation alexandru.tachici
  2 siblings, 1 reply; 7+ messages in thread
From: alexandru.tachici @ 2020-08-03 17:22 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: jic23

From: Stefan Popa <stefan.popa@analog.com>

By default, if all three channels (x, y, z) are enabled, sample sets of
concurrent 3-axis data is stored in the FIFO. This patch adds the option
to configure the FIFO to store peak acceleration (x, y and z) of every
over-threshold event. When pushing to iio buffer we push only enabled
axis data.

Signed-off-by: Stefan Popa <stefan.popa@analog.com>
---
 drivers/iio/accel/adxl372.c | 72 +++++++++++++++++++++++++++++++++++--
 1 file changed, 70 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 67b8817995c0..cce25cde6252 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -133,6 +133,9 @@
 
 /* The ADXL372 includes a deep, 512 sample FIFO buffer */
 #define ADXL372_FIFO_SIZE			512
+#define ADXL372_X_AXIS_EN(x)			(((x) >> 0) & 0x1)
+#define ADXL372_Y_AXIS_EN(x)			(((x) >> 1) & 0x1)
+#define ADXL372_Z_AXIS_EN(x)			(((x) >> 2) & 0x1)
 
 /*
  * At +/- 200g with 12-bit resolution, scale is computed as:
@@ -251,8 +254,10 @@ struct adxl372_state {
 	struct device			*dev;
 	struct regmap			*regmap;
 	struct iio_trigger		*dready_trig;
+	struct iio_trigger		*peak_datardy_trig;
 	enum adxl372_fifo_mode		fifo_mode;
 	enum adxl372_fifo_format	fifo_format;
+	unsigned int			fifo_axis_mask;
 	enum adxl372_op_mode		op_mode;
 	enum adxl372_act_proc_mode	act_proc_mode;
 	enum adxl372_odr		odr;
@@ -264,6 +269,7 @@ struct adxl372_state {
 	u8				int2_bitmask;
 	u16				watermark;
 	__be16				fifo_buf[ADXL372_FIFO_SIZE];
+	bool				peak_fifo_mode_en;
 };
 
 static const unsigned long adxl372_channel_masks[] = {
@@ -522,6 +528,22 @@ static int adxl372_get_status(struct adxl372_state *st,
 	return ret;
 }
 
+static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
+{
+	__be16	axis_sample[3];
+	int i = 0;
+
+	memset(axis_sample, 0, 3 * sizeof(__be16));
+	if (ADXL372_X_AXIS_EN(st->fifo_axis_mask))
+		axis_sample[i++] = sample[0];
+	if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask))
+		axis_sample[i++] = sample[1];
+	if (ADXL372_Z_AXIS_EN(st->fifo_axis_mask))
+		axis_sample[i++] = sample[2];
+
+	memcpy(sample, axis_sample, 3 * sizeof(__be16));
+}
+
 static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
 {
 	struct iio_poll_func *pf = p;
@@ -553,8 +575,12 @@ static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
 			goto err;
 
 		/* Each sample is 2 bytes */
-		for (i = 0; i < fifo_entries; i += st->fifo_set_size)
+		for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
+			/* filter peak detection data */
+			if (st->peak_fifo_mode_en)
+				adxl372_arrange_axis_data(st, &st->fifo_buf[i]);
 			iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
+		}
 	}
 err:
 	iio_trigger_notify_done(indio_dev->trig);
@@ -815,13 +841,22 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
 	}
 
 	st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
+	st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits;
 	st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
 					  indio_dev->masklength);
+
+	/* Configure the FIFO to store sets of impact event peak. */
+	if (st->peak_fifo_mode_en) {
+		st->fifo_set_size = 3;
+		st->fifo_format = ADXL372_XYZ_PEAK_FIFO;
+	}
+
 	/*
 	 * The 512 FIFO samples can be allotted in several ways, such as:
 	 * 170 sample sets of concurrent 3-axis data
 	 * 256 sample sets of concurrent 2-axis data (user selectable)
 	 * 512 sample sets of single-axis data
+	 * 170 sets of impact event peak (x, y, z)
 	 */
 	if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE)
 		st->watermark = (ADXL372_FIFO_SIZE  / st->fifo_set_size);
@@ -876,7 +911,7 @@ static int adxl372_validate_trigger(struct iio_dev *indio_dev,
 {
 	struct adxl372_state *st = iio_priv(indio_dev);
 
-	if (st->dready_trig != trig)
+	if (st->dready_trig != trig && st->peak_datardy_trig != trig)
 		return -EINVAL;
 
 	return 0;
@@ -887,6 +922,25 @@ static const struct iio_trigger_ops adxl372_trigger_ops = {
 	.set_trigger_state = adxl372_dready_trig_set_state,
 };
 
+static int adxl372_peak_dready_trig_set_state(struct iio_trigger *trig,
+					      bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct adxl372_state *st = iio_priv(indio_dev);
+
+	if (state)
+		st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;
+
+	st->peak_fifo_mode_en = state;
+
+	return adxl372_set_interrupts(st, st->int1_bitmask, 0);
+}
+
+static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = {
+	.validate_device = &iio_trigger_validate_own_device,
+	.set_trigger_state = adxl372_peak_dready_trig_set_state,
+};
+
 static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400");
 static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
 		       0444, adxl372_show_filter_freq_avail, NULL, 0);
@@ -965,13 +1019,27 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
 		if (st->dready_trig == NULL)
 			return -ENOMEM;
 
+		st->peak_datardy_trig = devm_iio_trigger_alloc(dev,
+							       "%s-dev%d-peak",
+							       indio_dev->name,
+							       indio_dev->id);
+		if (!st->peak_datardy_trig)
+			return -ENOMEM;
+
 		st->dready_trig->ops = &adxl372_trigger_ops;
+		st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops;
 		st->dready_trig->dev.parent = dev;
+		st->peak_datardy_trig->dev.parent = dev;
 		iio_trigger_set_drvdata(st->dready_trig, indio_dev);
+		iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev);
 		ret = devm_iio_trigger_register(dev, st->dready_trig);
 		if (ret < 0)
 			return ret;
 
+		ret = devm_iio_trigger_register(dev, st->peak_datardy_trig);
+		if (ret < 0)
+			return ret;
+
 		indio_dev->trig = iio_trigger_get(st->dready_trig);
 
 		ret = devm_request_threaded_irq(dev, st->irq,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/3] iio: accel: adxl372: add event interface
  2020-08-03 17:22 [PATCH v4 0/3] iio: accel: adxl372: add peak mode alexandru.tachici
  2020-08-03 17:22 ` [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO " alexandru.tachici
@ 2020-08-03 17:22 ` alexandru.tachici
  2020-08-09 13:19   ` Jonathan Cameron
  2020-08-03 17:22 ` [PATCH v4 3/3] docs: iio: Add adxl372 documentation alexandru.tachici
  2 siblings, 1 reply; 7+ messages in thread
From: alexandru.tachici @ 2020-08-03 17:22 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: jic23

From: Alexandru Tachici <alexandru.tachici@analog.com>

Currently the driver configures adxl372 to work in loop mode.
The inactivity and activity timings  decide how fast the chip
will loop through the awake and waiting states and the
thresholds on x,y,z axis decide when activity or inactivity
will be detected.

This patch adds standard events sysfs entries for the inactivity
and activity timings: thresh_falling_period/thresh_rising_period
and for the in_accel_x_thresh_falling/rising_value.

Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>
---
 drivers/iio/accel/adxl372.c | 256 +++++++++++++++++++++++++++++++++++-
 1 file changed, 250 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index cce25cde6252..644c409862b5 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -5,6 +5,7 @@
  * Copyright 2018 Analog Devices Inc.
  */
 
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -113,6 +114,11 @@
 #define ADXL372_STATUS_1_AWAKE(x)		(((x) >> 6) & 0x1)
 #define ADXL372_STATUS_1_ERR_USR_REGS(x)	(((x) >> 7) & 0x1)
 
+/* ADXL372_STATUS_2 */
+#define ADXL372_STATUS_2_INACT(x)		(((x) >> 4) & 0x1)
+#define ADXL372_STATUS_2_ACT(x)			(((x) >> 5) & 0x1)
+#define ADXL372_STATUS_2_AC2(x)			(((x) >> 6) & 0x1)
+
 /* ADXL372_INT1_MAP */
 #define ADXL372_INT1_MAP_DATA_RDY_MSK		BIT(0)
 #define ADXL372_INT1_MAP_DATA_RDY_MODE(x)	(((x) & 0x1) << 0)
@@ -131,6 +137,14 @@
 #define ADXL372_INT1_MAP_LOW_MSK		BIT(7)
 #define ADXL372_INT1_MAP_LOW_MODE(x)		(((x) & 0x1) << 7)
 
+/* ADX372_THRESH */
+#define ADXL372_THRESH_VAL_H_MSK		GENMASK(10, 3)
+#define ADXL372_THRESH_VAL_H_SEL(x)		\
+		FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x)
+#define ADXL372_THRESH_VAL_L_MSK		GENMASK(2, 0)
+#define ADXL372_THRESH_VAL_L_SEL(x)		\
+		FIELD_GET(ADXL372_THRESH_VAL_L_MSK, x)
+
 /* The ADXL372 includes a deep, 512 sample FIFO buffer */
 #define ADXL372_FIFO_SIZE			512
 #define ADXL372_X_AXIS_EN(x)			(((x) >> 0) & 0x1)
@@ -225,6 +239,22 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
 	{ BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
 };
 
+static const struct iio_event_spec adxl372_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+		.mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
+				      BIT(IIO_EV_INFO_ENABLE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+		.mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
+				      BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
 #define ADXL372_ACCEL_CHANNEL(index, reg, axis) {			\
 	.type = IIO_ACCEL,						\
 	.address = reg,							\
@@ -241,6 +271,8 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
 		.storagebits = 16,					\
 		.shift = 4,						\
 	},								\
+	.event_spec = adxl372_events,					\
+	.num_event_specs = ARRAY_SIZE(adxl372_events)			\
 }
 
 static const struct iio_chan_spec adxl372_channels[] = {
@@ -270,6 +302,7 @@ struct adxl372_state {
 	u16				watermark;
 	__be16				fifo_buf[ADXL372_FIFO_SIZE];
 	bool				peak_fifo_mode_en;
+	struct mutex			threshold_m; /* lock for threshold */
 };
 
 static const unsigned long adxl372_channel_masks[] = {
@@ -281,6 +314,49 @@ static const unsigned long adxl372_channel_masks[] = {
 	0
 };
 
+static ssize_t adxl372_read_threshold_value(struct iio_dev *indio_dev,
+					    unsigned int addr,
+					    u16 *threshold)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+	__be16 raw_regval;
+	u16 regval;
+	int ret;
+
+	ret = regmap_bulk_read(st->regmap, addr, &raw_regval,
+			       sizeof(raw_regval));
+	if (ret < 0)
+		return ret;
+
+	regval = be16_to_cpu(raw_regval);
+	regval >>= 5;
+
+	*threshold = regval;
+
+	return 0;
+}
+
+static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev,
+					     unsigned int addr,
+					     u16 threshold)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->threshold_m);
+	ret = regmap_write(st->regmap, addr,
+			   ADXL372_THRESH_VAL_H_SEL(threshold));
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5),
+				 ADXL372_THRESH_VAL_L_SEL(threshold) << 5);
+
+	mutex_unlock(&st->threshold_m);
+
+	return ret;
+}
+
 static int adxl372_read_axis(struct adxl372_state *st, u8 addr)
 {
 	__be16 regval;
@@ -544,6 +620,27 @@ static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
 	memcpy(sample, axis_sample, 3 * sizeof(__be16));
 }
 
+static void adxl372_push_event(struct iio_dev *indio_dev, s64 timestamp,
+			       u8 status2)
+{
+	unsigned int ev_dir = IIO_EV_DIR_NONE;
+
+	if (ADXL372_STATUS_2_ACT(status2))
+		ev_dir = IIO_EV_DIR_RISING;
+
+	if (ADXL372_STATUS_2_INACT(status2))
+		ev_dir = IIO_EV_DIR_FALLING;
+
+	if (ev_dir != IIO_EV_DIR_NONE)
+		iio_push_event(indio_dev,
+			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+						  0,
+						  IIO_MOD_X_OR_Y_OR_Z,
+						  IIO_EV_TYPE_THRESH,
+						  ev_dir),
+						  timestamp);
+}
+
 static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
 {
 	struct iio_poll_func *pf = p;
@@ -557,6 +654,8 @@ static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
 	if (ret < 0)
 		goto err;
 
+	adxl372_push_event(indio_dev, iio_get_time_ns(indio_dev), status2);
+
 	if (st->fifo_mode != ADXL372_FIFO_BYPASSED &&
 	    ADXL372_STATUS_1_FIFO_FULL(status1)) {
 		/*
@@ -748,6 +847,143 @@ static int adxl372_write_raw(struct iio_dev *indio_dev,
 	}
 }
 
+static int adxl372_read_event_value(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir,
+				    enum iio_event_info info,
+				    int *val, int *val2)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+	unsigned int addr;
+	u16 raw_value;
+	int ret;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		switch (dir) {
+		case IIO_EV_DIR_RISING:
+			addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index;
+			ret = adxl372_read_threshold_value(indio_dev, addr,
+							   &raw_value);
+			if (ret < 0)
+				return ret;
+			*val = raw_value * ADXL372_USCALE;
+			*val2 = 1000000;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_EV_DIR_FALLING:
+			addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index;
+			ret =  adxl372_read_threshold_value(indio_dev, addr,
+							    &raw_value);
+			if (ret < 0)
+				return ret;
+			*val = raw_value * ADXL372_USCALE;
+			*val2 = 1000000;
+			return IIO_VAL_FRACTIONAL;
+		default:
+			return -EINVAL;
+		}
+	case IIO_EV_INFO_PERIOD:
+		switch (dir) {
+		case IIO_EV_DIR_RISING:
+			*val = st->act_time_ms;
+			*val2 = 1000;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_EV_DIR_FALLING:
+			*val = st->inact_time_ms;
+			*val2 = 1000;
+			return IIO_VAL_FRACTIONAL;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adxl372_write_event_value(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir,
+				     enum iio_event_info info,
+				     int val, int val2)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+	unsigned int val_ms;
+	unsigned int addr;
+	u16 raw_val;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		raw_val = DIV_ROUND_UP(val * 1000000, ADXL372_USCALE);
+		switch (dir) {
+		case IIO_EV_DIR_RISING:
+			addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index;
+			return adxl372_write_threshold_value(indio_dev, addr,
+							     raw_val);
+		case IIO_EV_DIR_FALLING:
+			addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index;
+			return adxl372_write_threshold_value(indio_dev, addr,
+							     raw_val);
+		default:
+			return -EINVAL;
+		}
+	case IIO_EV_INFO_PERIOD:
+		val_ms = val * 1000 + DIV_ROUND_UP(val2, 1000);
+		switch (dir) {
+		case IIO_EV_DIR_RISING:
+			return adxl372_set_activity_time_ms(st, val_ms);
+		case IIO_EV_DIR_FALLING:
+			return adxl372_set_inactivity_time_ms(st, val_ms);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adxl372_read_event_config(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+
+	switch (dir) {
+	case IIO_EV_DIR_RISING:
+		return FIELD_GET(ADXL372_INT1_MAP_ACT_MSK, st->int1_bitmask);
+	case IIO_EV_DIR_FALLING:
+		return FIELD_GET(ADXL372_INT1_MAP_INACT_MSK, st->int1_bitmask);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adxl372_write_event_config(struct iio_dev *indio_dev,
+				      const struct iio_chan_spec *chan,
+				      enum iio_event_type type,
+				      enum iio_event_direction dir,
+				      int state)
+{
+	struct adxl372_state *st = iio_priv(indio_dev);
+
+	switch (dir) {
+	case IIO_EV_DIR_RISING:
+		set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_ACT_MSK,
+			      ADXL372_INT1_MAP_ACT_MODE(state));
+		break;
+	case IIO_EV_DIR_FALLING:
+		set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_INACT_MSK,
+			      ADXL372_INT1_MAP_INACT_MODE(state));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return adxl372_set_interrupts(st, st->int1_bitmask, 0);
+}
+
 static ssize_t adxl372_show_filter_freq_avail(struct device *dev,
 					      struct device_attribute *attr,
 					      char *buf)
@@ -824,7 +1060,8 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
 	if (ret < 0)
 		return ret;
 
-	ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
+	st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;
+	ret = adxl372_set_interrupts(st, st->int1_bitmask, 0);
 	if (ret < 0)
 		goto err;
 
@@ -866,7 +1103,8 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
 	ret = adxl372_configure_fifo(st);
 	if (ret < 0) {
 		st->fifo_mode = ADXL372_FIFO_BYPASSED;
-		adxl372_set_interrupts(st, 0, 0);
+		st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK;
+		adxl372_set_interrupts(st, st->int1_bitmask, 0);
 		goto err;
 	}
 
@@ -881,7 +1119,8 @@ static int adxl372_buffer_predisable(struct iio_dev *indio_dev)
 {
 	struct adxl372_state *st = iio_priv(indio_dev);
 
-	adxl372_set_interrupts(st, 0, 0);
+	st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK;
+	adxl372_set_interrupts(st, st->int1_bitmask, 0);
 	st->fifo_mode = ADXL372_FIFO_BYPASSED;
 	adxl372_configure_fifo(st);
 
@@ -898,12 +1137,11 @@ static int adxl372_dready_trig_set_state(struct iio_trigger *trig,
 {
 	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
 	struct adxl372_state *st = iio_priv(indio_dev);
-	unsigned long int mask = 0;
 
 	if (state)
-		mask = ADXL372_INT1_MAP_FIFO_FULL_MSK;
+		st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;
 
-	return adxl372_set_interrupts(st, mask, 0);
+	return adxl372_set_interrupts(st, st->int1_bitmask, 0);
 }
 
 static int adxl372_validate_trigger(struct iio_dev *indio_dev,
@@ -960,6 +1198,10 @@ static const struct iio_info adxl372_info = {
 	.attrs = &adxl372_attrs_group,
 	.read_raw = adxl372_read_raw,
 	.write_raw = adxl372_write_raw,
+	.read_event_config = adxl372_read_event_config,
+	.write_event_config = adxl372_write_event_config,
+	.read_event_value = adxl372_read_event_value,
+	.write_event_value = adxl372_write_event_value,
 	.debugfs_reg_access = &adxl372_reg_access,
 	.hwfifo_set_watermark = adxl372_set_watermark,
 };
@@ -988,6 +1230,8 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
 	st->regmap = regmap;
 	st->irq = irq;
 
+	mutex_init(&st->threshold_m);
+
 	indio_dev->channels = adxl372_channels;
 	indio_dev->num_channels = ARRAY_SIZE(adxl372_channels);
 	indio_dev->available_scan_masks = adxl372_channel_masks;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 3/3] docs: iio: Add adxl372 documentation
  2020-08-03 17:22 [PATCH v4 0/3] iio: accel: adxl372: add peak mode alexandru.tachici
  2020-08-03 17:22 ` [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO " alexandru.tachici
  2020-08-03 17:22 ` [PATCH v4 2/3] iio: accel: adxl372: add event interface alexandru.tachici
@ 2020-08-03 17:22 ` alexandru.tachici
  2020-08-09 13:21   ` Jonathan Cameron
  2 siblings, 1 reply; 7+ messages in thread
From: alexandru.tachici @ 2020-08-03 17:22 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: jic23

From: Alexandru Tachici <alexandru.tachici@analog.com>

Add documentation for adxl372 3-axis accelerometer.

Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>
---
 Documentation/iio/adxl372.rst | 46 +++++++++++++++++++++++++++++++++++
 Documentation/iio/index.rst   |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Documentation/iio/adxl372.rst

diff --git a/Documentation/iio/adxl372.rst b/Documentation/iio/adxl372.rst
new file mode 100644
index 000000000000..f8fe5f438400
--- /dev/null
+++ b/Documentation/iio/adxl372.rst
@@ -0,0 +1,46 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver adxl372
+=====================
+
+Supported chips:
+  * Analog Devices ADXL372
+    Prefix: 'adxl372'
+    Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL372.pdf
+
+Author: Stefan Popa <stefan.popa@analog.com>
+
+
+Description
+-----------
+
+The ADXL372 is an ultralow power, 3-axis, ±200 g MEMS accelerometer
+that consumes 22 μA at a 3200 Hz output data rate (ODR).
+
+The ADXL372 provides 12-bit output data at 100 mg/LSB scale factor.
+
+Using the FIFO Buffer
+---------------------
+
+The ADXL372 includes a deep, 512 sample FIFO buffer.
+The 512 FIFO samples can be allotted in several ways, such as the following:
+
+    170 sample sets of concurrent 3-axis data
+    256 sample sets of concurrent 2-axis data (see scan_elements/in_accel_*_en)
+    512 sample sets of single-axis data
+    170 sets of impact event peak (x, y, z)
+
+By default when using the buffer adxl372 will store all
+acceleration data. To store only the peak acceleration data, the user must
+select the peak data trigger: adxl372-dev0-peak
+
+The user can set the thresholds for each axis for activity and inactivity in:
+- events/in_accel_*_thresh_rising_value
+- events/in_accel_*_thresh_falling_value
+
+An inactivity/activity event is detected when acceleration in all enabled
+axes remains below/above a specified threshold for a specified time. The user
+can set these timings in:
+- events/thresh_falling_period
+- events/thresh_rising_period
+
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 58b7a4ebac51..3d0acb1eef86 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -10,3 +10,4 @@ Industrial I/O
    iio_configfs
 
    ep93xx_adc
+   adxl372.rst
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO peak mode
  2020-08-03 17:22 ` [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO " alexandru.tachici
@ 2020-08-09 13:11   ` Jonathan Cameron
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2020-08-09 13:11 UTC (permalink / raw)
  To: alexandru.tachici; +Cc: linux-iio, linux-kernel

On Mon, 3 Aug 2020 20:22:17 +0300
<alexandru.tachici@analog.com> wrote:

> From: Stefan Popa <stefan.popa@analog.com>
> 
> By default, if all three channels (x, y, z) are enabled, sample sets of
> concurrent 3-axis data is stored in the FIFO. This patch adds the option
> to configure the FIFO to store peak acceleration (x, y and z) of every
> over-threshold event. When pushing to iio buffer we push only enabled
> axis data.
> 
> Signed-off-by: Stefan Popa <stefan.popa@analog.com>
Hi Stefan,

A few things inline I missed on previous reads.

Jonathan

> ---
>  drivers/iio/accel/adxl372.c | 72 +++++++++++++++++++++++++++++++++++--
>  1 file changed, 70 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
> index 67b8817995c0..cce25cde6252 100644
> --- a/drivers/iio/accel/adxl372.c
> +++ b/drivers/iio/accel/adxl372.c
> @@ -133,6 +133,9 @@
>  
>  /* The ADXL372 includes a deep, 512 sample FIFO buffer */
>  #define ADXL372_FIFO_SIZE			512
> +#define ADXL372_X_AXIS_EN(x)			(((x) >> 0) & 0x1)
I hadn't noticed this before, but this is a slightly odd construct.
Would be more normal to check against the  mask directly, so
something like...

((x) & BIT(0))
((x) & BIT(1))

> +#define ADXL372_Y_AXIS_EN(x)			(((x) >> 1) & 0x1)
> +#define ADXL372_Z_AXIS_EN(x)			(((x) >> 2) & 0x1)
>  
>  /*
>   * At +/- 200g with 12-bit resolution, scale is computed as:
> @@ -251,8 +254,10 @@ struct adxl372_state {
>  	struct device			*dev;
>  	struct regmap			*regmap;
>  	struct iio_trigger		*dready_trig;
> +	struct iio_trigger		*peak_datardy_trig;
>  	enum adxl372_fifo_mode		fifo_mode;
>  	enum adxl372_fifo_format	fifo_format;
> +	unsigned int			fifo_axis_mask;
>  	enum adxl372_op_mode		op_mode;
>  	enum adxl372_act_proc_mode	act_proc_mode;
>  	enum adxl372_odr		odr;
> @@ -264,6 +269,7 @@ struct adxl372_state {
>  	u8				int2_bitmask;
>  	u16				watermark;
>  	__be16				fifo_buf[ADXL372_FIFO_SIZE];
> +	bool				peak_fifo_mode_en;
>  };
>  
>  static const unsigned long adxl372_channel_masks[] = {
> @@ -522,6 +528,22 @@ static int adxl372_get_status(struct adxl372_state *st,
>  	return ret;
>  }
>  
> +static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
> +{
> +	__be16	axis_sample[3];
> +	int i = 0;
> +
> +	memset(axis_sample, 0, 3 * sizeof(__be16));
> +	if (ADXL372_X_AXIS_EN(st->fifo_axis_mask))
> +		axis_sample[i++] = sample[0];
> +	if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask))
> +		axis_sample[i++] = sample[1];
> +	if (ADXL372_Z_AXIS_EN(st->fifo_axis_mask))
> +		axis_sample[i++] = sample[2];
> +
> +	memcpy(sample, axis_sample, 3 * sizeof(__be16));
> +}
> +
>  static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
>  {
>  	struct iio_poll_func *pf = p;
> @@ -553,8 +575,12 @@ static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
>  			goto err;
>  
>  		/* Each sample is 2 bytes */
> -		for (i = 0; i < fifo_entries; i += st->fifo_set_size)
> +		for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
> +			/* filter peak detection data */
> +			if (st->peak_fifo_mode_en)
> +				adxl372_arrange_axis_data(st, &st->fifo_buf[i]);
>  			iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
> +		}
>  	}
>  err:
>  	iio_trigger_notify_done(indio_dev->trig);
> @@ -815,13 +841,22 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
>  	}
>  
>  	st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
> +	st->fifo_axis_mask = adxl372_axis_lookup_table[i].bits;
>  	st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
>  					  indio_dev->masklength);
> +
> +	/* Configure the FIFO to store sets of impact event peak. */
> +	if (st->peak_fifo_mode_en) {
> +		st->fifo_set_size = 3;
> +		st->fifo_format = ADXL372_XYZ_PEAK_FIFO;
> +	}
> +
>  	/*
>  	 * The 512 FIFO samples can be allotted in several ways, such as:
>  	 * 170 sample sets of concurrent 3-axis data
>  	 * 256 sample sets of concurrent 2-axis data (user selectable)
>  	 * 512 sample sets of single-axis data
> +	 * 170 sets of impact event peak (x, y, z)
>  	 */
>  	if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE)
>  		st->watermark = (ADXL372_FIFO_SIZE  / st->fifo_set_size);
> @@ -876,7 +911,7 @@ static int adxl372_validate_trigger(struct iio_dev *indio_dev,
>  {
>  	struct adxl372_state *st = iio_priv(indio_dev);
>  
> -	if (st->dready_trig != trig)
> +	if (st->dready_trig != trig && st->peak_datardy_trig != trig)
>  		return -EINVAL;
>  
>  	return 0;
> @@ -887,6 +922,25 @@ static const struct iio_trigger_ops adxl372_trigger_ops = {
>  	.set_trigger_state = adxl372_dready_trig_set_state,
>  };
>  
> +static int adxl372_peak_dready_trig_set_state(struct iio_trigger *trig,
> +					      bool state)
> +{
> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +
> +	if (state)
> +		st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;

The original driver usage of int1_bitmask seems rather odd.
There is a state variable for it, but it's not ever used.

Here we seem to set this bit, but it is never cleared anywhere.

If we don't actually need to store it globally, perhaps drop the
variable in st?


> +
> +	st->peak_fifo_mode_en = state;
> +
> +	return adxl372_set_interrupts(st, st->int1_bitmask, 0);
> +}
> +
> +static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = {
> +	.validate_device = &iio_trigger_validate_own_device,
> +	.set_trigger_state = adxl372_peak_dready_trig_set_state,
> +};
> +
>  static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400");
>  static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
>  		       0444, adxl372_show_filter_freq_avail, NULL, 0);
> @@ -965,13 +1019,27 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
>  		if (st->dready_trig == NULL)
>  			return -ENOMEM;
>  
> +		st->peak_datardy_trig = devm_iio_trigger_alloc(dev,
> +							       "%s-dev%d-peak",
> +							       indio_dev->name,
> +							       indio_dev->id);
> +		if (!st->peak_datardy_trig)
> +			return -ENOMEM;
> +
>  		st->dready_trig->ops = &adxl372_trigger_ops;
> +		st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops;
>  		st->dready_trig->dev.parent = dev;
> +		st->peak_datardy_trig->dev.parent = dev;
>  		iio_trigger_set_drvdata(st->dready_trig, indio_dev);
> +		iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev);
>  		ret = devm_iio_trigger_register(dev, st->dready_trig);
>  		if (ret < 0)
>  			return ret;
>  
> +		ret = devm_iio_trigger_register(dev, st->peak_datardy_trig);
> +		if (ret < 0)
> +			return ret;
> +
>  		indio_dev->trig = iio_trigger_get(st->dready_trig);
>  
>  		ret = devm_request_threaded_irq(dev, st->irq,


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 2/3] iio: accel: adxl372: add event interface
  2020-08-03 17:22 ` [PATCH v4 2/3] iio: accel: adxl372: add event interface alexandru.tachici
@ 2020-08-09 13:19   ` Jonathan Cameron
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2020-08-09 13:19 UTC (permalink / raw)
  To: alexandru.tachici; +Cc: linux-iio, linux-kernel

On Mon, 3 Aug 2020 20:22:18 +0300
<alexandru.tachici@analog.com> wrote:

> From: Alexandru Tachici <alexandru.tachici@analog.com>
> 
> Currently the driver configures adxl372 to work in loop mode.
> The inactivity and activity timings  decide how fast the chip
> will loop through the awake and waiting states and the
> thresholds on x,y,z axis decide when activity or inactivity
> will be detected.
> 
> This patch adds standard events sysfs entries for the inactivity
> and activity timings: thresh_falling_period/thresh_rising_period
> and for the in_accel_x_thresh_falling/rising_value.
> 
> Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>

Ah. This makes sense of some of the elements in the previous patch that
didn't stand alone.  I'd take the easy approach and just combine the two
patches.  Alternative would be to move some of bits that are refactored
to use int1_bitmask into a precursor to both patches that is just a
refactor with no functional changes.  At the moment it's not possible
to  review the patches independently.

Jonathan


> ---
>  drivers/iio/accel/adxl372.c | 256 +++++++++++++++++++++++++++++++++++-
>  1 file changed, 250 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
> index cce25cde6252..644c409862b5 100644
> --- a/drivers/iio/accel/adxl372.c
> +++ b/drivers/iio/accel/adxl372.c
> @@ -5,6 +5,7 @@
>   * Copyright 2018 Analog Devices Inc.
>   */
>  
> +#include <linux/bitfield.h>
>  #include <linux/bitops.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
> @@ -113,6 +114,11 @@
>  #define ADXL372_STATUS_1_AWAKE(x)		(((x) >> 6) & 0x1)
>  #define ADXL372_STATUS_1_ERR_USR_REGS(x)	(((x) >> 7) & 0x1)
>  
> +/* ADXL372_STATUS_2 */
> +#define ADXL372_STATUS_2_INACT(x)		(((x) >> 4) & 0x1)
> +#define ADXL372_STATUS_2_ACT(x)			(((x) >> 5) & 0x1)
> +#define ADXL372_STATUS_2_AC2(x)			(((x) >> 6) & 0x1)
> +
>  /* ADXL372_INT1_MAP */
>  #define ADXL372_INT1_MAP_DATA_RDY_MSK		BIT(0)
>  #define ADXL372_INT1_MAP_DATA_RDY_MODE(x)	(((x) & 0x1) << 0)
> @@ -131,6 +137,14 @@
>  #define ADXL372_INT1_MAP_LOW_MSK		BIT(7)
>  #define ADXL372_INT1_MAP_LOW_MODE(x)		(((x) & 0x1) << 7)
>  
> +/* ADX372_THRESH */
> +#define ADXL372_THRESH_VAL_H_MSK		GENMASK(10, 3)
> +#define ADXL372_THRESH_VAL_H_SEL(x)		\
> +		FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x)
#define ADXL372_THRESH_VAL_H_SEL(x)		FIELD_GET(ADXL372_THRESH_VAL_H_MSK, x)

Given we can now got up to 100 chars, let us making this more readable and keep them
on single lines.

> +#define ADXL372_THRESH_VAL_L_MSK		GENMASK(2, 0)
> +#define ADXL372_THRESH_VAL_L_SEL(x)		\
> +		FIELD_GET(ADXL372_THRESH_VAL_L_MSK, x)
> +
>  /* The ADXL372 includes a deep, 512 sample FIFO buffer */
>  #define ADXL372_FIFO_SIZE			512
>  #define ADXL372_X_AXIS_EN(x)			(((x) >> 0) & 0x1)
> @@ -225,6 +239,22 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
>  	{ BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
>  };
>  
> +static const struct iio_event_spec adxl372_events[] = {
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
> +				      BIT(IIO_EV_INFO_ENABLE),
> +	}, {
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD) |
> +				      BIT(IIO_EV_INFO_ENABLE),
> +	},
> +};
> +
>  #define ADXL372_ACCEL_CHANNEL(index, reg, axis) {			\
>  	.type = IIO_ACCEL,						\
>  	.address = reg,							\
> @@ -241,6 +271,8 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
>  		.storagebits = 16,					\
>  		.shift = 4,						\
>  	},								\
> +	.event_spec = adxl372_events,					\
> +	.num_event_specs = ARRAY_SIZE(adxl372_events)			\
>  }
>  
>  static const struct iio_chan_spec adxl372_channels[] = {
> @@ -270,6 +302,7 @@ struct adxl372_state {
>  	u16				watermark;
>  	__be16				fifo_buf[ADXL372_FIFO_SIZE];
>  	bool				peak_fifo_mode_en;
> +	struct mutex			threshold_m; /* lock for threshold */
>  };
>  
>  static const unsigned long adxl372_channel_masks[] = {
> @@ -281,6 +314,49 @@ static const unsigned long adxl372_channel_masks[] = {
>  	0
>  };
>  
> +static ssize_t adxl372_read_threshold_value(struct iio_dev *indio_dev,
> +					    unsigned int addr,
> +					    u16 *threshold)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +	__be16 raw_regval;
> +	u16 regval;
> +	int ret;
> +
> +	ret = regmap_bulk_read(st->regmap, addr, &raw_regval,
> +			       sizeof(raw_regval));
> +	if (ret < 0)
> +		return ret;
> +
> +	regval = be16_to_cpu(raw_regval);
> +	regval >>= 5;
> +
> +	*threshold = regval;
> +
> +	return 0;
> +}
> +
> +static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev,
> +					     unsigned int addr,
> +					     u16 threshold)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&st->threshold_m);
> +	ret = regmap_write(st->regmap, addr,
> +			   ADXL372_THRESH_VAL_H_SEL(threshold));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5),
> +				 ADXL372_THRESH_VAL_L_SEL(threshold) << 5);
> +
> +	mutex_unlock(&st->threshold_m);
> +
> +	return ret;
> +}
> +
>  static int adxl372_read_axis(struct adxl372_state *st, u8 addr)
>  {
>  	__be16 regval;
> @@ -544,6 +620,27 @@ static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
>  	memcpy(sample, axis_sample, 3 * sizeof(__be16));
>  }
>  
> +static void adxl372_push_event(struct iio_dev *indio_dev, s64 timestamp,
> +			       u8 status2)
> +{
> +	unsigned int ev_dir = IIO_EV_DIR_NONE;
> +
> +	if (ADXL372_STATUS_2_ACT(status2))
> +		ev_dir = IIO_EV_DIR_RISING;
> +
> +	if (ADXL372_STATUS_2_INACT(status2))
> +		ev_dir = IIO_EV_DIR_FALLING;
> +
> +	if (ev_dir != IIO_EV_DIR_NONE)
> +		iio_push_event(indio_dev,
> +			       IIO_MOD_EVENT_CODE(IIO_ACCEL,
> +						  0,
> +						  IIO_MOD_X_OR_Y_OR_Z,
> +						  IIO_EV_TYPE_THRESH,
> +						  ev_dir),
> +						  timestamp);
> +}
> +
>  static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
>  {
>  	struct iio_poll_func *pf = p;
> @@ -557,6 +654,8 @@ static irqreturn_t adxl372_trigger_handler(int irq, void  *p)
>  	if (ret < 0)
>  		goto err;
>  
> +	adxl372_push_event(indio_dev, iio_get_time_ns(indio_dev), status2);
> +
>  	if (st->fifo_mode != ADXL372_FIFO_BYPASSED &&
>  	    ADXL372_STATUS_1_FIFO_FULL(status1)) {
>  		/*
> @@ -748,6 +847,143 @@ static int adxl372_write_raw(struct iio_dev *indio_dev,
>  	}
>  }
>  
> +static int adxl372_read_event_value(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir,
> +				    enum iio_event_info info,
> +				    int *val, int *val2)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +	unsigned int addr;
> +	u16 raw_value;
> +	int ret;
> +
> +	switch (info) {
> +	case IIO_EV_INFO_VALUE:
> +		switch (dir) {
> +		case IIO_EV_DIR_RISING:
> +			addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index;
> +			ret = adxl372_read_threshold_value(indio_dev, addr,
> +							   &raw_value);
> +			if (ret < 0)
> +				return ret;
> +			*val = raw_value * ADXL372_USCALE;
> +			*val2 = 1000000;
> +			return IIO_VAL_FRACTIONAL;
> +		case IIO_EV_DIR_FALLING:
> +			addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index;
> +			ret =  adxl372_read_threshold_value(indio_dev, addr,
> +							    &raw_value);
> +			if (ret < 0)
> +				return ret;
> +			*val = raw_value * ADXL372_USCALE;
> +			*val2 = 1000000;
> +			return IIO_VAL_FRACTIONAL;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_EV_INFO_PERIOD:
> +		switch (dir) {
> +		case IIO_EV_DIR_RISING:
> +			*val = st->act_time_ms;
> +			*val2 = 1000;
> +			return IIO_VAL_FRACTIONAL;
> +		case IIO_EV_DIR_FALLING:
> +			*val = st->inact_time_ms;
> +			*val2 = 1000;
> +			return IIO_VAL_FRACTIONAL;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int adxl372_write_event_value(struct iio_dev *indio_dev,
> +				     const struct iio_chan_spec *chan,
> +				     enum iio_event_type type,
> +				     enum iio_event_direction dir,
> +				     enum iio_event_info info,
> +				     int val, int val2)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +	unsigned int val_ms;
> +	unsigned int addr;
> +	u16 raw_val;
> +
> +	switch (info) {
> +	case IIO_EV_INFO_VALUE:
> +		raw_val = DIV_ROUND_UP(val * 1000000, ADXL372_USCALE);
> +		switch (dir) {
> +		case IIO_EV_DIR_RISING:
> +			addr = ADXL372_X_THRESH_ACT_H + 2 * chan->scan_index;
> +			return adxl372_write_threshold_value(indio_dev, addr,
> +							     raw_val);

I respinning this, perhaps take advantage of more relaxed view on line length and
put these all on oneline.

> +		case IIO_EV_DIR_FALLING:
> +			addr = ADXL372_X_THRESH_INACT_H + 2 * chan->scan_index;
> +			return adxl372_write_threshold_value(indio_dev, addr,
> +							     raw_val);
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_EV_INFO_PERIOD:
> +		val_ms = val * 1000 + DIV_ROUND_UP(val2, 1000);
> +		switch (dir) {
> +		case IIO_EV_DIR_RISING:
> +			return adxl372_set_activity_time_ms(st, val_ms);
> +		case IIO_EV_DIR_FALLING:
> +			return adxl372_set_inactivity_time_ms(st, val_ms);
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int adxl372_read_event_config(struct iio_dev *indio_dev,
> +				     const struct iio_chan_spec *chan,
> +				     enum iio_event_type type,
> +				     enum iio_event_direction dir)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +
> +	switch (dir) {
> +	case IIO_EV_DIR_RISING:
> +		return FIELD_GET(ADXL372_INT1_MAP_ACT_MSK, st->int1_bitmask);
> +	case IIO_EV_DIR_FALLING:
> +		return FIELD_GET(ADXL372_INT1_MAP_INACT_MSK, st->int1_bitmask);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int adxl372_write_event_config(struct iio_dev *indio_dev,
> +				      const struct iio_chan_spec *chan,
> +				      enum iio_event_type type,
> +				      enum iio_event_direction dir,
> +				      int state)
> +{
> +	struct adxl372_state *st = iio_priv(indio_dev);
> +
> +	switch (dir) {
> +	case IIO_EV_DIR_RISING:
> +		set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_ACT_MSK,
> +			      ADXL372_INT1_MAP_ACT_MODE(state));
> +		break;
> +	case IIO_EV_DIR_FALLING:
> +		set_mask_bits(&st->int1_bitmask, ADXL372_INT1_MAP_INACT_MSK,
> +			      ADXL372_INT1_MAP_INACT_MODE(state));
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return adxl372_set_interrupts(st, st->int1_bitmask, 0);
> +}
> +
>  static ssize_t adxl372_show_filter_freq_avail(struct device *dev,
>  					      struct device_attribute *attr,
>  					      char *buf)
> @@ -824,7 +1060,8 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
>  	if (ret < 0)
>  		return ret;
>  
> -	ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
> +	st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;
> +	ret = adxl372_set_interrupts(st, st->int1_bitmask, 0);
>  	if (ret < 0)
>  		goto err;
>  
> @@ -866,7 +1103,8 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
>  	ret = adxl372_configure_fifo(st);
>  	if (ret < 0) {
>  		st->fifo_mode = ADXL372_FIFO_BYPASSED;
> -		adxl372_set_interrupts(st, 0, 0);
> +		st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK;
> +		adxl372_set_interrupts(st, st->int1_bitmask, 0);
>  		goto err;
>  	}
>  
> @@ -881,7 +1119,8 @@ static int adxl372_buffer_predisable(struct iio_dev *indio_dev)
>  {
>  	struct adxl372_state *st = iio_priv(indio_dev);
>  
> -	adxl372_set_interrupts(st, 0, 0);
> +	st->int1_bitmask &= ~ADXL372_INT1_MAP_FIFO_FULL_MSK;
> +	adxl372_set_interrupts(st, st->int1_bitmask, 0);
>  	st->fifo_mode = ADXL372_FIFO_BYPASSED;
>  	adxl372_configure_fifo(st);
>  
> @@ -898,12 +1137,11 @@ static int adxl372_dready_trig_set_state(struct iio_trigger *trig,
>  {
>  	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
>  	struct adxl372_state *st = iio_priv(indio_dev);
> -	unsigned long int mask = 0;
>  
>  	if (state)
> -		mask = ADXL372_INT1_MAP_FIFO_FULL_MSK;
> +		st->int1_bitmask |= ADXL372_INT1_MAP_FIFO_FULL_MSK;
>  
> -	return adxl372_set_interrupts(st, mask, 0);
> +	return adxl372_set_interrupts(st, st->int1_bitmask, 0);

Ah. Previous code makes a bit more sense with this patch at least
ensuring int1_bitmask is used here as well.  However don't we need
to clear it somewhere?

>  }
>  
>  static int adxl372_validate_trigger(struct iio_dev *indio_dev,
> @@ -960,6 +1198,10 @@ static const struct iio_info adxl372_info = {
>  	.attrs = &adxl372_attrs_group,
>  	.read_raw = adxl372_read_raw,
>  	.write_raw = adxl372_write_raw,
> +	.read_event_config = adxl372_read_event_config,
> +	.write_event_config = adxl372_write_event_config,
> +	.read_event_value = adxl372_read_event_value,
> +	.write_event_value = adxl372_write_event_value,
>  	.debugfs_reg_access = &adxl372_reg_access,
>  	.hwfifo_set_watermark = adxl372_set_watermark,
>  };
> @@ -988,6 +1230,8 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
>  	st->regmap = regmap;
>  	st->irq = irq;
>  
> +	mutex_init(&st->threshold_m);
> +
>  	indio_dev->channels = adxl372_channels;
>  	indio_dev->num_channels = ARRAY_SIZE(adxl372_channels);
>  	indio_dev->available_scan_masks = adxl372_channel_masks;


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 3/3] docs: iio: Add adxl372 documentation
  2020-08-03 17:22 ` [PATCH v4 3/3] docs: iio: Add adxl372 documentation alexandru.tachici
@ 2020-08-09 13:21   ` Jonathan Cameron
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2020-08-09 13:21 UTC (permalink / raw)
  To: alexandru.tachici; +Cc: linux-iio, linux-kernel

On Mon, 3 Aug 2020 20:22:19 +0300
<alexandru.tachici@analog.com> wrote:

> From: Alexandru Tachici <alexandru.tachici@analog.com>
> 
> Add documentation for adxl372 3-axis accelerometer.
> 
> Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>

This is ABI docs, so I'd normally expect them in
Documentation/ABI/testing/sysfs-bus-iio-accel-adxl372
rather than as part of the main docs.

I can kind of see why you want to take advantage of more free form
docs, but I'd definitely want to see them in the ABI docs first.
Once that is done, then we can see if there is anything left that
needs to be documented like you have here.

Thanks,

Jonathan


> ---
>  Documentation/iio/adxl372.rst | 46 +++++++++++++++++++++++++++++++++++
>  Documentation/iio/index.rst   |  1 +
>  2 files changed, 47 insertions(+)
>  create mode 100644 Documentation/iio/adxl372.rst
> 
> diff --git a/Documentation/iio/adxl372.rst b/Documentation/iio/adxl372.rst
> new file mode 100644
> index 000000000000..f8fe5f438400
> --- /dev/null
> +++ b/Documentation/iio/adxl372.rst
> @@ -0,0 +1,46 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +Kernel driver adxl372
> +=====================
> +
> +Supported chips:
> +  * Analog Devices ADXL372
> +    Prefix: 'adxl372'
> +    Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL372.pdf
> +
> +Author: Stefan Popa <stefan.popa@analog.com>
> +
> +
> +Description
> +-----------
> +
> +The ADXL372 is an ultralow power, 3-axis, ±200 g MEMS accelerometer
> +that consumes 22 μA at a 3200 Hz output data rate (ODR).
> +
> +The ADXL372 provides 12-bit output data at 100 mg/LSB scale factor.
> +
> +Using the FIFO Buffer
> +---------------------
> +
> +The ADXL372 includes a deep, 512 sample FIFO buffer.
> +The 512 FIFO samples can be allotted in several ways, such as the following:
> +
> +    170 sample sets of concurrent 3-axis data
> +    256 sample sets of concurrent 2-axis data (see scan_elements/in_accel_*_en)
> +    512 sample sets of single-axis data
> +    170 sets of impact event peak (x, y, z)
> +
> +By default when using the buffer adxl372 will store all
> +acceleration data. To store only the peak acceleration data, the user must
> +select the peak data trigger: adxl372-dev0-peak
> +
> +The user can set the thresholds for each axis for activity and inactivity in:
> +- events/in_accel_*_thresh_rising_value
> +- events/in_accel_*_thresh_falling_value
> +
> +An inactivity/activity event is detected when acceleration in all enabled
> +axes remains below/above a specified threshold for a specified time. The user
> +can set these timings in:
> +- events/thresh_falling_period
> +- events/thresh_rising_period
> +
> diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
> index 58b7a4ebac51..3d0acb1eef86 100644
> --- a/Documentation/iio/index.rst
> +++ b/Documentation/iio/index.rst
> @@ -10,3 +10,4 @@ Industrial I/O
>     iio_configfs
>  
>     ep93xx_adc
> +   adxl372.rst


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-08-09 13:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-03 17:22 [PATCH v4 0/3] iio: accel: adxl372: add peak mode alexandru.tachici
2020-08-03 17:22 ` [PATCH v4 1/3] iio: accel: adxl372: Add support for FIFO " alexandru.tachici
2020-08-09 13:11   ` Jonathan Cameron
2020-08-03 17:22 ` [PATCH v4 2/3] iio: accel: adxl372: add event interface alexandru.tachici
2020-08-09 13:19   ` Jonathan Cameron
2020-08-03 17:22 ` [PATCH v4 3/3] docs: iio: Add adxl372 documentation alexandru.tachici
2020-08-09 13:21   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).