2022-05-05 16:57:25

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 00/10] iio: accel: bma400: Add buffer, step and activity/inactivity

This patch series adds trigger buffer support with data ready interrupt,
separate channel for step counter, an event for step change interrupt,
activity recognition and activity/inactivity event support.

changes since v4
1/10 - Added Reviewed-by tag.

2/10 - No change.

3/10 - No change.

4/10 - Using __aligned(IIO_ALIGN) instead of ____cacheline_aligned.
Added another entry to bma400_avail_scan_masks() only for
accelerometer axis reading. Now temperature will be read only if
requested by user.
Added Reviewed-by.

5/10 - Checking regmap_update_bits() and return if error occrus.

6/10 - Using IIO_MOD_EVENT_CODE() instead of IIO_EVENT_CODE().
Pushing all the events that occurred by checking status register
and then in the end returning HANDLED so that none of the events
are missed.

7/10 - Using IIO_MOD_EVENT_CODE() instead of IIO_EVENT_CODE().

8/10 - Using set_mask_bits() instead of FIELD_PREP().

9/10 - Added new channel for tap and modifiers for single and double tap.

10/10- Added bma400 single and double tap support.

Removed debugfs patch.

changes since v3
1. Removed all the unnecessary mutex locking for regmap.
2. Corrected the mutex locking and unlocking for device private data
members.
3. Mutex locking and unlocking is used to protect the device private
structure members.
4. Using DMA safe buffer for regmap_bulk_write() and regmap_bulk_read().

1/9: Fixed the comment.

3/9: Added () for the function name in the comment.

4/9: Handling error cases with goto in bma400_trigger_handler().
Mutex locking and unlocking is used to protect the data->buffer.
Using DMA safe buffer for regmap_bulk_read().
Mutex locking and unlocking is used to protect the data->status in
bma400_interrupt.

5/9: Using DMA safe buffers to read steps value by allocating memory internally.
Using DMA safe buffers for regmap_bulk_write().
Removed the lock for regmap().

6/9: Removed the duplication of code for enabling step, added function to handle
the step enable.

7/9: Removed the lock for regmap().
Mutex locking and unlocking is used to protect the data members.

8/9: Removed the lock for regmap().

9/9. Added __be16 duration in struct bma400_data.
Fixed the warning - impossible condition '(reg < 0) => (0-255 < 0)'
Fixed error: call to __compiletime_assert_272
Reported-by: kernel test robot <[email protected]>
Reported-by: Dan Carpenter <[email protected]>

changes since v2
1. Reordering of header includes in the separate patch.
2. Matching the IIO syntax for multiline comment.
3. Following the preference in the interrupt handler for returning.
4. Add support for activity recognition.
5. Add support for debugfs to access registers from userspace.
6. Add support for activity and inactivity events

changes since v1
1. Added comment section that describes the math for scale calculation.
2. Added separate devm_add_action_or_reset() calls to disable regulator
and to put the sensor in power down mode.
3. Remove the err_reg_disable and out, goto labels and returning directly
if error occurs.
4. Added mutex calls while putting sensor in power down.
5. Added ___cacheline_aligned for device data.
6. Ordering the header includes.
7. Handling erroneous and spurious interrupts in the interrupt handler
by returning IRQ_NONE.
8. Using dev_err_probe() instead of dev_err().
9. Configured the interrupt to open drain.
10. Using le16_to_cpu() to fix the sparse warning.
11. Checking the step change event is enabled or not.
12. Enabling the step change event will also enable the step channel.
13. Using FIELD_GET() instead of bitwise operation.
14. Removal of dead code in the _event_config().


Jagath Jog J (10):
iio: accel: bma400: Fix the scale min and max macro values
iio: accel: bma400: Reordering of header files
iio: accel: bma400: conversion to device-managed function
iio: accel: bma400: Add triggered buffer support
iio: accel: bma400: Add separate channel for step counter
iio: accel: bma400: Add step change event
iio: accel: bma400: Add activity recognition support
iio: accel: bma400: Add support for activity and inactivity events
iio: Add channel for tap and new modifiers for single and double tap
iio: accel: bma400: Add support for single and double tap events

Documentation/ABI/testing/sysfs-bus-iio | 11 +
drivers/iio/accel/Kconfig | 2 +
drivers/iio/accel/bma400.h | 61 +-
drivers/iio/accel/bma400_core.c | 886 ++++++++++++++++++++++--
drivers/iio/accel/bma400_i2c.c | 10 +-
drivers/iio/accel/bma400_spi.c | 8 +-
drivers/iio/industrialio-core.c | 3 +
include/uapi/linux/iio/types.h | 3 +
tools/iio/iio_event_monitor.c | 6 +
9 files changed, 923 insertions(+), 67 deletions(-)

--
2.17.1



2022-05-06 02:57:16

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 05/10] iio: accel: bma400: Add separate channel for step counter

Added channel for step counter which can be enable or disable
through the sysfs interface.

Signed-off-by: Jagath Jog J <[email protected]>
---
drivers/iio/accel/bma400.h | 2 +
drivers/iio/accel/bma400_core.c | 69 +++++++++++++++++++++++++++++++--
2 files changed, 67 insertions(+), 4 deletions(-)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 907e1a6c0a38..32c08f8b0b98 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -53,6 +53,8 @@
#define BMA400_STEP_CNT1_REG 0x16
#define BMA400_STEP_CNT3_REG 0x17
#define BMA400_STEP_STAT_REG 0x18
+#define BMA400_STEP_INT_MSK BIT(0)
+#define BMA400_STEP_RAW_LEN 0x03

/*
* Read-write configuration registers
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 67e102c097bc..fdb7e8bd7b27 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -19,6 +19,9 @@
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <asm/unaligned.h>

#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -74,6 +77,7 @@ struct bma400_data {
int oversampling_ratio;
int scale;
struct iio_trigger *trig;
+ int steps_enabled;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -209,6 +213,12 @@ static const struct iio_chan_spec bma400_channels[] = {
.endianness = IIO_LE,
},
},
+ {
+ .type = IIO_STEPS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_ENABLE),
+ .scan_index = -1, /* No buffer support */
+ },
IIO_CHAN_SOFT_TIMESTAMP(4),
};

@@ -577,6 +587,40 @@ static int bma400_set_power_mode(struct bma400_data *data,
return 0;
}

+static int bma400_enable_steps(struct bma400_data *data, int val)
+{
+ int ret;
+
+ if (data->steps_enabled == val)
+ return 0;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG,
+ BMA400_STEP_INT_MSK,
+ FIELD_PREP(BMA400_STEP_INT_MSK, !!val));
+ if (ret)
+ return ret;
+ data->steps_enabled = val;
+ return ret;
+}
+
+static int bma400_get_steps_reg(struct bma400_data *data, int *val)
+{
+ u8 *steps_raw;
+ int ret;
+
+ steps_raw = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL);
+ if (!steps_raw)
+ return -ENOMEM;
+
+ ret = regmap_bulk_read(data->regmap, BMA400_STEP_CNT0_REG,
+ steps_raw, BMA400_STEP_RAW_LEN);
+ if (ret)
+ return ret;
+ *val = get_unaligned_le24(steps_raw);
+ kfree(steps_raw);
+ return IIO_VAL_INT;
+}
+
static void bma400_init_tables(void)
{
int raw;
@@ -716,10 +760,17 @@ static int bma400_read_raw(struct iio_dev *indio_dev,

switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
- mutex_lock(&data->mutex);
- ret = bma400_get_temp_reg(data, val, val2);
- mutex_unlock(&data->mutex);
- return ret;
+ switch (chan->type) {
+ case IIO_TEMP:
+ mutex_lock(&data->mutex);
+ ret = bma400_get_temp_reg(data, val, val2);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_STEPS:
+ return bma400_get_steps_reg(data, val);
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->mutex);
ret = bma400_get_accel_reg(data, chan, val);
@@ -760,6 +811,9 @@ static int bma400_read_raw(struct iio_dev *indio_dev,

*val = data->oversampling_ratio;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_ENABLE:
+ *val = data->steps_enabled;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -825,6 +879,11 @@ static int bma400_write_raw(struct iio_dev *indio_dev,
ret = bma400_set_accel_oversampling_ratio(data, val);
mutex_unlock(&data->mutex);
return ret;
+ case IIO_CHAN_INFO_ENABLE:
+ mutex_lock(&data->mutex);
+ ret = bma400_enable_steps(data, val);
+ mutex_unlock(&data->mutex);
+ return ret;
default:
return -EINVAL;
}
@@ -841,6 +900,8 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_ENABLE:
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
--
2.17.1


2022-05-06 09:00:33

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 09/10] iio: Add channel for tap and new modifiers for single and double tap

Add new channel type for tap and also add new modifiers for single and
double tap. This channel and modifiers may be used by accelerometer
sensors to express single and double tap events. For directional tap,
modifiers like IIO_MOD_(X/Y/Z) can be used along with rising and
falling direction.

Signed-off-by: Jagath Jog J <[email protected]>
---
Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++
drivers/iio/industrialio-core.c | 3 +++
include/uapi/linux/iio/types.h | 3 +++
tools/iio/iio_event_monitor.c | 6 ++++++
4 files changed, 23 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index d4ccc68fdcf0..bf2d10d6ad9b 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2030,3 +2030,14 @@ Description:
Available range for the forced calibration value, expressed as:

- a range specified as "[min step max]"
+
+What: /sys/.../events/in_tap_single_change_en
+What: /sys/.../events/in_tap_double_change_en
+KernelVersion: 5.19
+Contact: [email protected]
+Description:
+ Accelerometer device detects single or double taps and generate
+ events when threshold for minimum tap amplitide passes.
+ E.g. a single tap event is generated when acceleration value
+ crosses the minimum tap amplitude value set. Where tap threshold
+ value is set by using in_tap_change_value.
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index e1ed44dec2ab..9b0d7bbd07fc 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
[IIO_MASSCONCENTRATION] = "massconcentration",
+ [IIO_TAP] = "tap"
};

static const char * const iio_modifier_names[] = {
@@ -134,6 +135,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_ETHANOL] = "ethanol",
[IIO_MOD_H2] = "h2",
[IIO_MOD_O2] = "o2",
+ [IIO_MOD_TAP_SINGLE] = "single",
+ [IIO_MOD_TAP_DOUBLE] = "double",
};

/* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 472cead10d8d..d1e61c84e0d5 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -47,6 +47,7 @@ enum iio_chan_type {
IIO_POSITIONRELATIVE,
IIO_PHASE,
IIO_MASSCONCENTRATION,
+ IIO_TAP,
};

enum iio_modifier {
@@ -95,6 +96,8 @@ enum iio_modifier {
IIO_MOD_ETHANOL,
IIO_MOD_H2,
IIO_MOD_O2,
+ IIO_MOD_TAP_SINGLE,
+ IIO_MOD_TAP_DOUBLE,
};

enum iio_event_type {
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index 2f4581658859..7fa7d4285f40 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
[IIO_MASSCONCENTRATION] = "massconcentration",
+ [IIO_TAP] = "tap",
};

static const char * const iio_ev_type_text[] = {
@@ -122,6 +123,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_PM4] = "pm4",
[IIO_MOD_PM10] = "pm10",
[IIO_MOD_O2] = "o2",
+ [IIO_MOD_TAP_SINGLE] = "single",
+ [IIO_MOD_TAP_DOUBLE] = "double",
};

static bool event_is_known(struct iio_event_data *event)
@@ -164,6 +167,7 @@ static bool event_is_known(struct iio_event_data *event)
case IIO_POSITIONRELATIVE:
case IIO_PHASE:
case IIO_MASSCONCENTRATION:
+ case IIO_TAP:
break;
default:
return false;
@@ -215,6 +219,8 @@ static bool event_is_known(struct iio_event_data *event)
case IIO_MOD_PM4:
case IIO_MOD_PM10:
case IIO_MOD_O2:
+ case IIO_MOD_TAP_SINGLE:
+ case IIO_MOD_TAP_DOUBLE:
break;
default:
return false;
--
2.17.1


2022-05-08 12:13:33

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 04/10] iio: accel: bma400: Add triggered buffer support

Added trigger buffer support to read continuous acceleration
and temperature data from device with data ready interrupt which
is mapped to INT1 pin.

Signed-off-by: Jagath Jog J <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
---
drivers/iio/accel/Kconfig | 2 +
drivers/iio/accel/bma400.h | 10 +-
drivers/iio/accel/bma400_core.c | 177 +++++++++++++++++++++++++++++++-
drivers/iio/accel/bma400_i2c.c | 2 +-
drivers/iio/accel/bma400_spi.c | 2 +-
5 files changed, 185 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index eac3f02662ae..958097814232 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -204,6 +204,8 @@ config BMA220
config BMA400
tristate "Bosch BMA400 3-Axis Accelerometer Driver"
select REGMAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
select BMA400_I2C if I2C
select BMA400_SPI if SPI
help
diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 1c8c47a9a317..907e1a6c0a38 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -62,6 +62,13 @@
#define BMA400_ACC_CONFIG2_REG 0x1b
#define BMA400_CMD_REG 0x7e

+/* Interrupt registers */
+#define BMA400_INT_CONFIG0_REG 0x1f
+#define BMA400_INT_CONFIG1_REG 0x20
+#define BMA400_INT1_MAP_REG 0x21
+#define BMA400_INT_IO_CTRL_REG 0x24
+#define BMA400_INT_DRDY_MSK BIT(7)
+
/* Chip ID of BMA 400 devices found in the chip ID register. */
#define BMA400_ID_REG_VAL 0x90

@@ -111,6 +118,7 @@

extern const struct regmap_config bma400_regmap_config;

-int bma400_probe(struct device *dev, struct regmap *regmap, const char *name);
+int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name);

#endif
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 07674d89d978..67e102c097bc 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -11,6 +11,7 @@
* - Create channel for sensor time
*/

+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -20,6 +21,10 @@
#include <linux/regulator/consumer.h>

#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>

#include "bma400.h"

@@ -46,6 +51,13 @@ enum bma400_power_mode {
POWER_MODE_INVALID = 0x03,
};

+enum bma400_scan {
+ BMA400_ACCL_X,
+ BMA400_ACCL_Y,
+ BMA400_ACCL_Z,
+ BMA400_TEMP,
+};
+
struct bma400_sample_freq {
int hz;
int uhz;
@@ -61,6 +73,14 @@ struct bma400_data {
struct bma400_sample_freq sample_freq;
int oversampling_ratio;
int scale;
+ struct iio_trigger *trig;
+ /* Correct time stamp alignment */
+ struct {
+ __le16 buff[3];
+ u8 temperature;
+ s64 ts __aligned(8);
+ } buffer __aligned(IIO_ALIGN);
+ __le16 status;
};

static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
@@ -152,7 +172,7 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
{ }
};

-#define BMA400_ACC_CHANNEL(_axis) { \
+#define BMA400_ACC_CHANNEL(_index, _axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
@@ -164,17 +184,32 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.ext_info = bma400_ext_info, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
}

static const struct iio_chan_spec bma400_channels[] = {
- BMA400_ACC_CHANNEL(X),
- BMA400_ACC_CHANNEL(Y),
- BMA400_ACC_CHANNEL(Z),
+ BMA400_ACC_CHANNEL(0, X),
+ BMA400_ACC_CHANNEL(1, Y),
+ BMA400_ACC_CHANNEL(2, Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 8,
+ .storagebits = 8,
+ .endianness = IIO_LE,
+ },
},
+ IIO_CHAN_SOFT_TIMESTAMP(4),
};

static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2)
@@ -659,6 +694,10 @@ static int bma400_init(struct bma400_data *data)
if (ret)
return ret;

+ /* Configure INT1 pin to open drain */
+ ret = regmap_write(data->regmap, BMA400_INT_IO_CTRL_REG, 0x06);
+ if (ret)
+ return ret;
/*
* Once the interrupt engine is supported we might use the
* data_src_reg, but for now ensure this is set to the
@@ -807,6 +846,31 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}

+static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG,
+ BMA400_INT_DRDY_MSK,
+ FIELD_PREP(BMA400_INT_DRDY_MSK, state));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG,
+ BMA400_INT_DRDY_MSK,
+ FIELD_PREP(BMA400_INT_DRDY_MSK, state));
+}
+
+static const unsigned long bma400_avail_scan_masks[] = {
+ BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z),
+ BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z)
+ | BIT(BMA400_TEMP),
+ 0
+};
+
static const struct iio_info bma400_info = {
.read_raw = bma400_read_raw,
.read_avail = bma400_read_avail,
@@ -814,7 +878,78 @@ static const struct iio_info bma400_info = {
.write_raw_get_fmt = bma400_write_raw_get_fmt,
};

-int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
+static const struct iio_trigger_ops bma400_trigger_ops = {
+ .set_trigger_state = &bma400_data_rdy_trigger_set_state,
+ .validate_device = &iio_trigger_validate_own_device,
+};
+
+static irqreturn_t bma400_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret, temp;
+
+ /* Lock to protect the data->buffer */
+ mutex_lock(&data->mutex);
+
+ /* bulk read six registers, with the base being the LSB register */
+ ret = regmap_bulk_read(data->regmap, BMA400_X_AXIS_LSB_REG,
+ &data->buffer.buff, sizeof(data->buffer.buff));
+ if (ret)
+ goto unlock_err;
+
+ if (test_bit(BMA400_TEMP, indio_dev->active_scan_mask)) {
+ ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &temp);
+ if (ret)
+ goto unlock_err;
+
+ data->buffer.temperature = temp;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
+ iio_get_time_ns(indio_dev));
+
+ mutex_unlock(&data->mutex);
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+
+unlock_err:
+ mutex_unlock(&data->mutex);
+ return IRQ_NONE;
+}
+
+static irqreturn_t bma400_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret;
+
+ /* Lock to protect the data->status */
+ mutex_lock(&data->mutex);
+ ret = regmap_bulk_read(data->regmap, BMA400_INT_STAT0_REG,
+ &data->status,
+ sizeof(data->status));
+ /*
+ * if none of the bit is set in the status register then it is
+ * spurious interrupt.
+ */
+ if (ret || !data->status)
+ goto unlock_err;
+
+ if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
+ mutex_unlock(&data->mutex);
+ iio_trigger_poll_chained(data->trig);
+ return IRQ_HANDLED;
+ }
+
+unlock_err:
+ mutex_unlock(&data->mutex);
+ return IRQ_NONE;
+}
+
+int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name)
{
struct iio_dev *indio_dev;
struct bma400_data *data;
@@ -841,8 +976,40 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
indio_dev->info = &bma400_info;
indio_dev->channels = bma400_channels;
indio_dev->num_channels = ARRAY_SIZE(bma400_channels);
+ indio_dev->available_scan_masks = bma400_avail_scan_masks;
indio_dev->modes = INDIO_DIRECT_MODE;

+ if (irq > 0) {
+ data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = &bma400_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+
+ ret = devm_iio_trigger_register(data->dev, data->trig);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "iio trigger register fail\n");
+
+ indio_dev->trig = iio_trigger_get(data->trig);
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ &bma400_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "request irq %d failed\n", irq);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ &bma400_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "iio triggered buffer setup failed\n");
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c
index 4f6e01a3b3a1..1ba2a982ea73 100644
--- a/drivers/iio/accel/bma400_i2c.c
+++ b/drivers/iio/accel/bma400_i2c.c
@@ -24,7 +24,7 @@ static int bma400_i2c_probe(struct i2c_client *client,
return PTR_ERR(regmap);
}

- return bma400_probe(&client->dev, regmap, id->name);
+ return bma400_probe(&client->dev, regmap, client->irq, id->name);
}

static const struct i2c_device_id bma400_i2c_ids[] = {
diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c
index 28e240400a3f..ec13c044b304 100644
--- a/drivers/iio/accel/bma400_spi.c
+++ b/drivers/iio/accel/bma400_spi.c
@@ -84,7 +84,7 @@ static int bma400_spi_probe(struct spi_device *spi)
if (ret)
dev_err(&spi->dev, "Failed to read chip id register\n");

- return bma400_probe(&spi->dev, regmap, id->name);
+ return bma400_probe(&spi->dev, regmap, spi->irq, id->name);
}

static const struct spi_device_id bma400_spi_ids[] = {
--
2.17.1


2022-05-09 03:23:14

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 07/10] iio: accel: bma400: Add activity recognition support

Add support for activity recognition like STILL, WALKING, RUNNING
and these events are pushed to the userspace whenever the STEP
interrupt occurs.

Signed-off-by: Jagath Jog J <[email protected]>
---
drivers/iio/accel/bma400_core.c | 85 +++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)

diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 50932fc1c854..1e4923064b63 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -67,6 +67,12 @@ struct bma400_sample_freq {
int uhz;
};

+enum bma400_activity {
+ BMA400_STILL,
+ BMA400_WALKING,
+ BMA400_RUNNING,
+};
+
struct bma400_data {
struct device *dev;
struct regmap *regmap;
@@ -80,6 +86,7 @@ struct bma400_data {
struct iio_trigger *trig;
int steps_enabled;
bool step_event_en;
+ bool activity_event_en;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -184,6 +191,12 @@ static const struct iio_event_spec bma400_step_detect_event = {
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
};

+static const struct iio_event_spec bma400_activity_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
+};
+
#define BMA400_ACC_CHANNEL(_index, _axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -205,6 +218,16 @@ static const struct iio_event_spec bma400_step_detect_event = {
}, \
}

+#define BMA400_ACTIVITY_CHANNEL(_chan2) { \
+ .type = IIO_ACTIVITY, \
+ .modified = 1, \
+ .channel2 = _chan2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = -1, /* No buffer support */ \
+ .event_spec = &bma400_activity_event, \
+ .num_event_specs = 1, \
+}
+
static const struct iio_chan_spec bma400_channels[] = {
BMA400_ACC_CHANNEL(0, X),
BMA400_ACC_CHANNEL(1, Y),
@@ -229,6 +252,9 @@ static const struct iio_chan_spec bma400_channels[] = {
.event_spec = &bma400_step_detect_event,
.num_event_specs = 1,
},
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_STILL),
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
IIO_CHAN_SOFT_TIMESTAMP(4),
};

@@ -669,6 +695,20 @@ static void bma400_power_disable(void *data_ptr)
ERR_PTR(ret));
}

+static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity)
+{
+ switch (activity) {
+ case BMA400_STILL:
+ return IIO_MOD_STILL;
+ case BMA400_WALKING:
+ return IIO_MOD_WALKING;
+ case BMA400_RUNNING:
+ return IIO_MOD_RUNNING;
+ default:
+ return IIO_NO_MOD;
+ }
+}
+
static int bma400_init(struct bma400_data *data)
{
unsigned int val;
@@ -766,6 +806,7 @@ static int bma400_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct bma400_data *data = iio_priv(indio_dev);
+ unsigned int activity;
int ret;

switch (mask) {
@@ -778,6 +819,21 @@ static int bma400_read_raw(struct iio_dev *indio_dev,
return ret;
case IIO_STEPS:
return bma400_get_steps_reg(data, val);
+ case IIO_ACTIVITY:
+ ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG,
+ &activity);
+ if (ret)
+ return ret;
+ /*
+ * The device does not support confidence value levels,
+ * so we will always have 100% for current activity and
+ * 0% for the others.
+ */
+ if (chan->channel2 == bma400_act_to_mod(activity))
+ *val = 100;
+ else
+ *val = 0;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -927,6 +983,8 @@ static int bma400_read_event_config(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_STEPS:
return data->step_event_en;
+ case IIO_ACTIVITY:
+ return data->activity_event_en;
default:
return -EINVAL;
}
@@ -964,6 +1022,18 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
ret = bma400_steps_event_enable(data, state);
mutex_unlock(&data->mutex);
return ret;
+ case IIO_ACTIVITY:
+ mutex_lock(&data->mutex);
+ if (!data->step_event_en) {
+ ret = bma400_steps_event_enable(data, true);
+ if (ret) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ }
+ data->activity_event_en = state;
+ mutex_unlock(&data->mutex);
+ return 0;
default:
return -EINVAL;
}
@@ -1049,6 +1119,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
struct iio_dev *indio_dev = private;
struct bma400_data *data = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
+ unsigned int act;
int ret;

/* Lock to protect the data->status */
@@ -1069,6 +1140,20 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
IIO_EV_TYPE_CHANGE,
IIO_EV_DIR_NONE),
timestamp);
+
+ if (data->activity_event_en) {
+ ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG,
+ &act);
+ if (ret)
+ goto unlock_err;
+
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0,
+ bma400_act_to_mod(act),
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_NONE),
+ timestamp);
+ }
}

if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
--
2.17.1


2022-05-09 04:34:53

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 02/10] iio: accel: bma400: Reordering of header files

Reordering of header files and removing the iio/sysfs.h since
custom attributes are not being used in the driver.

Signed-off-by: Jagath Jog J <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
---
drivers/iio/accel/bma400_core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 043002fe6f63..25ad1f7339bc 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -13,14 +13,14 @@

#include <linux/bitops.h>
#include <linux/device.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>

+#include <linux/iio/iio.h>
+
#include "bma400.h"

/*
--
2.17.1


2022-05-09 06:09:10

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 08/10] iio: accel: bma400: Add support for activity and inactivity events

Add support for activity and inactivity events for all axis based on the
threshold, duration and hysteresis value set from the userspace. INT1 pin
is used to interrupt and event is pushed to userspace.

Signed-off-by: Jagath Jog J <[email protected]>
---
drivers/iio/accel/bma400.h | 11 ++
drivers/iio/accel/bma400_core.c | 232 +++++++++++++++++++++++++++++++-
2 files changed, 242 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 0faa40fdbbf8..e8f802a82300 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -94,6 +94,17 @@
#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25
#define BMA400_ACC_ODR_MIN_HZ 12

+/* Generic interrupts register */
+#define BMA400_GEN1INT_CONFIG0 0x3f
+#define BMA400_GEN2INT_CONFIG0 0x4A
+#define BMA400_GEN_CONFIG1_OFF 0x01
+#define BMA400_GEN_CONFIG2_OFF 0x02
+#define BMA400_GEN_CONFIG3_OFF 0x03
+#define BMA400_GEN_CONFIG31_OFF 0x04
+#define BMA400_INT_GEN1_MSK BIT(2)
+#define BMA400_INT_GEN2_MSK BIT(3)
+#define BMA400_GEN_HYST_MSK GENMASK(1, 0)
+
/*
* BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
* converting to micro values for +-2g range.
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 1e4923064b63..8faff72625b3 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -87,6 +87,7 @@ struct bma400_data {
int steps_enabled;
bool step_event_en;
bool activity_event_en;
+ unsigned int generic_event_en;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -94,6 +95,7 @@ struct bma400_data {
s64 ts __aligned(8);
} buffer __aligned(IIO_ALIGN);
__le16 status;
+ __be16 duration;
};

static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
@@ -197,6 +199,25 @@ static const struct iio_event_spec bma400_activity_event = {
.mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
};

+static const struct iio_event_spec bma400_accel_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
#define BMA400_ACC_CHANNEL(_index, _axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -216,6 +237,8 @@ static const struct iio_event_spec bma400_activity_event = {
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
+ .event_spec = bma400_accel_event, \
+ .num_event_specs = ARRAY_SIZE(bma400_accel_event) \
}

#define BMA400_ACTIVITY_CHANNEL(_chan2) { \
@@ -981,6 +1004,17 @@ static int bma400_read_event_config(struct iio_dev *indio_dev,
struct bma400_data *data = iio_priv(indio_dev);

switch (chan->type) {
+ case IIO_ACCEL:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return FIELD_GET(BMA400_INT_GEN1_MSK,
+ data->generic_event_en);
+ case IIO_EV_DIR_FALLING:
+ return FIELD_GET(BMA400_INT_GEN2_MSK,
+ data->generic_event_en);
+ default:
+ return -EINVAL;
+ }
case IIO_STEPS:
return data->step_event_en;
case IIO_ACTIVITY:
@@ -1008,6 +1042,65 @@ static int bma400_steps_event_enable(struct bma400_data *data, int state)
return 0;
}

+static int bma400_activity_event_en(struct bma400_data *data,
+ enum iio_event_direction dir,
+ int state)
+{
+ int ret, reg, msk, value, field_value;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ reg = BMA400_GEN1INT_CONFIG0;
+ msk = BMA400_INT_GEN1_MSK;
+ value = 2;
+ set_mask_bits(&field_value, BMA400_INT_GEN1_MSK,
+ FIELD_PREP(BMA400_INT_GEN1_MSK, state));
+ break;
+ case IIO_EV_DIR_FALLING:
+ reg = BMA400_GEN2INT_CONFIG0;
+ msk = BMA400_INT_GEN2_MSK;
+ value = 0;
+ set_mask_bits(&field_value, BMA400_INT_GEN2_MSK,
+ FIELD_PREP(BMA400_INT_GEN2_MSK, state));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enabling all axis for interrupt evaluation */
+ ret = regmap_write(data->regmap, reg, 0xF8);
+ if (ret)
+ return ret;
+
+ /* OR combination of all axis for interrupt evaluation */
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG1_OFF, value);
+ if (ret)
+ return ret;
+
+ /* Initial value to avoid interrupts while enabling*/
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG2_OFF, 0x0A);
+ if (ret)
+ return ret;
+
+ /* Initial duration value to avoid interrupts while enabling*/
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG31_OFF, 0x0F);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG, msk,
+ field_value);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG, msk,
+ field_value);
+ if (ret)
+ return ret;
+
+ set_mask_bits(&data->generic_event_en, msk, field_value);
+ return 0;
+}
+
static int bma400_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@@ -1017,6 +1110,11 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
int ret;

switch (chan->type) {
+ case IIO_ACCEL:
+ mutex_lock(&data->mutex);
+ ret = bma400_activity_event_en(data, dir, state);
+ mutex_unlock(&data->mutex);
+ return ret;
case IIO_STEPS:
mutex_lock(&data->mutex);
ret = bma400_steps_event_enable(data, state);
@@ -1039,6 +1137,122 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
}
}

+static int get_gen_config_reg(enum iio_event_direction dir)
+{
+ switch (dir) {
+ case IIO_EV_DIR_FALLING:
+ return BMA400_GEN2INT_CONFIG0;
+ case IIO_EV_DIR_RISING:
+ return BMA400_GEN1INT_CONFIG0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_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 bma400_data *data = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ reg = get_gen_config_reg(dir);
+ if (reg < 0)
+ return -EINVAL;
+
+ *val2 = 0;
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(data->regmap,
+ reg + BMA400_GEN_CONFIG2_OFF,
+ val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ mutex_lock(&data->mutex);
+ ret = regmap_bulk_read(data->regmap,
+ reg + BMA400_GEN_CONFIG3_OFF,
+ &data->duration,
+ sizeof(data->duration));
+ if (ret) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ *val = be16_to_cpu(data->duration);
+ mutex_unlock(&data->mutex);
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = regmap_read(data->regmap, reg, val);
+ if (ret)
+ return ret;
+ *val = FIELD_GET(BMA400_GEN_HYST_MSK, *val);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_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 bma400_data *data = iio_priv(indio_dev);
+ int reg, ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ reg = get_gen_config_reg(dir);
+ if (reg < 0)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 1 || val > 255)
+ return -EINVAL;
+
+ return regmap_write(data->regmap,
+ reg + BMA400_GEN_CONFIG2_OFF,
+ val);
+ case IIO_EV_INFO_PERIOD:
+ if (val < 1 || val > 65535)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ put_unaligned_be16(val, &data->duration);
+ ret = regmap_bulk_write(data->regmap,
+ reg + BMA400_GEN_CONFIG3_OFF,
+ &data->duration,
+ sizeof(data->duration));
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_EV_INFO_HYSTERESIS:
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap, reg,
+ BMA400_GEN_HYST_MSK,
+ FIELD_PREP(BMA400_GEN_HYST_MSK,
+ val));
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@@ -1071,6 +1285,8 @@ static const struct iio_info bma400_info = {
.write_raw_get_fmt = bma400_write_raw_get_fmt,
.read_event_config = bma400_read_event_config,
.write_event_config = bma400_write_event_config,
+ .write_event_value = bma400_write_event_value,
+ .read_event_value = bma400_read_event_value,
};

static const struct iio_trigger_ops bma400_trigger_ops = {
@@ -1119,7 +1335,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
struct iio_dev *indio_dev = private;
struct bma400_data *data = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
- unsigned int act;
+ unsigned int act, ev_dir = IIO_EV_DIR_NONE;
int ret;

/* Lock to protect the data->status */
@@ -1134,6 +1350,20 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
if (ret || !data->status)
goto unlock_err;

+ if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status)))
+ ev_dir = IIO_EV_DIR_RISING;
+
+ if (FIELD_GET(BMA400_INT_GEN2_MSK, le16_to_cpu(data->status)))
+ 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_MAG, ev_dir),
+ timestamp);
+ }
+
if (FIELD_GET(BMA400_STEP_STAT_MASK, le16_to_cpu(data->status))) {
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
--
2.17.1


2022-05-09 06:51:45

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 06/10] iio: accel: bma400: Add step change event

Added support for event when there is a detection of step change.
INT1 pin is used to interrupt and event is pushed to userspace.

Signed-off-by: Jagath Jog J <[email protected]>
---
drivers/iio/accel/bma400.h | 2 +
drivers/iio/accel/bma400_core.c | 76 +++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 32c08f8b0b98..0faa40fdbbf8 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -39,6 +39,7 @@
#define BMA400_INT_STAT0_REG 0x0e
#define BMA400_INT_STAT1_REG 0x0f
#define BMA400_INT_STAT2_REG 0x10
+#define BMA400_INT12_MAP_REG 0x23

/* Temperature register */
#define BMA400_TEMP_DATA_REG 0x11
@@ -55,6 +56,7 @@
#define BMA400_STEP_STAT_REG 0x18
#define BMA400_STEP_INT_MSK BIT(0)
#define BMA400_STEP_RAW_LEN 0x03
+#define BMA400_STEP_STAT_MASK GENMASK(9, 8)

/*
* Read-write configuration registers
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index fdb7e8bd7b27..50932fc1c854 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -25,6 +25,7 @@

#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -78,6 +79,7 @@ struct bma400_data {
int scale;
struct iio_trigger *trig;
int steps_enabled;
+ bool step_event_en;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -176,6 +178,12 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
{ }
};

+static const struct iio_event_spec bma400_step_detect_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+};
+
#define BMA400_ACC_CHANNEL(_index, _axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -218,6 +226,8 @@ static const struct iio_chan_spec bma400_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_ENABLE),
.scan_index = -1, /* No buffer support */
+ .event_spec = &bma400_step_detect_event,
+ .num_event_specs = 1,
},
IIO_CHAN_SOFT_TIMESTAMP(4),
};
@@ -907,6 +917,58 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}

+static int bma400_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 bma400_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_STEPS:
+ return data->step_event_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_steps_event_enable(struct bma400_data *data, int state)
+{
+ int ret;
+
+ ret = bma400_enable_steps(data, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG,
+ BMA400_STEP_INT_MSK,
+ FIELD_PREP(BMA400_STEP_INT_MSK,
+ state));
+ if (ret)
+ return ret;
+ data->step_event_en = state;
+ return 0;
+}
+
+static int bma400_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 bma400_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_STEPS:
+ mutex_lock(&data->mutex);
+ ret = bma400_steps_event_enable(data, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@@ -937,6 +999,8 @@ static const struct iio_info bma400_info = {
.read_avail = bma400_read_avail,
.write_raw = bma400_write_raw,
.write_raw_get_fmt = bma400_write_raw_get_fmt,
+ .read_event_config = bma400_read_event_config,
+ .write_event_config = bma400_write_event_config,
};

static const struct iio_trigger_ops bma400_trigger_ops = {
@@ -984,6 +1048,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct bma400_data *data = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns(indio_dev);
int ret;

/* Lock to protect the data->status */
@@ -998,12 +1063,23 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
if (ret || !data->status)
goto unlock_err;

+ if (FIELD_GET(BMA400_STEP_STAT_MASK, le16_to_cpu(data->status))) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_NONE),
+ timestamp);
+ }
+
if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
mutex_unlock(&data->mutex);
iio_trigger_poll_chained(data->trig);
return IRQ_HANDLED;
}

+ mutex_unlock(&data->mutex);
+ return IRQ_HANDLED;
+
unlock_err:
mutex_unlock(&data->mutex);
return IRQ_NONE;
--
2.17.1


2022-05-09 07:12:09

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v5 05/10] iio: accel: bma400: Add separate channel for step counter

On Thu, 5 May 2022 19:00:16 +0530
Jagath Jog J <[email protected]> wrote:

> Added channel for step counter which can be enable or disable
> through the sysfs interface.
>
> Signed-off-by: Jagath Jog J <[email protected]>
> ---
>
> +static int bma400_enable_steps(struct bma400_data *data, int val)
> +{
> + int ret;
> +
> + if (data->steps_enabled == val)
> + return 0;
> +
> + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG,
> + BMA400_STEP_INT_MSK,
> + FIELD_PREP(BMA400_STEP_INT_MSK, !!val));
This gives a dubious x & !y warning

So I've changed it to val ? 1 : 0

Jonathan

> + if (ret)
> + return ret;
> + data->steps_enabled = val;
> + return ret;
> +}

2022-05-09 07:43:55

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 03/10] iio: accel: bma400: conversion to device-managed function

This is a conversion to device-managed by using devm_iio_device_register()
inside probe function. Previously the bma400 was not put into power down
mode in some error paths in probe where it now is, but that should cause
no harm.

The dev_set_drvdata() call, bma400_remove() function and hooks in the I2C
and SPI driver struct is removed as devm_iio_device_register() function is
used to automatically unregister on driver detach.

Signed-off-by: Jagath Jog J <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
---
drivers/iio/accel/bma400.h | 2 -
drivers/iio/accel/bma400_core.c | 77 ++++++++++++++++-----------------
drivers/iio/accel/bma400_i2c.c | 8 ----
drivers/iio/accel/bma400_spi.c | 6 ---
4 files changed, 38 insertions(+), 55 deletions(-)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index 80330c7ce17f..1c8c47a9a317 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -113,6 +113,4 @@ extern const struct regmap_config bma400_regmap_config;

int bma400_probe(struct device *dev, struct regmap *regmap, const char *name);

-void bma400_remove(struct device *dev);
-
#endif
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 25ad1f7339bc..07674d89d978 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -560,6 +560,26 @@ static void bma400_init_tables(void)
}
}

+static void bma400_regulators_disable(void *data_ptr)
+{
+ struct bma400_data *data = data_ptr;
+
+ regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
+}
+
+static void bma400_power_disable(void *data_ptr)
+{
+ struct bma400_data *data = data_ptr;
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bma400_set_power_mode(data, POWER_MODE_SLEEP);
+ mutex_unlock(&data->mutex);
+ if (ret)
+ dev_warn(data->dev, "Failed to put device into sleep mode (%pe)\n",
+ ERR_PTR(ret));
+}
+
static int bma400_init(struct bma400_data *data)
{
unsigned int val;
@@ -569,13 +589,12 @@ static int bma400_init(struct bma400_data *data)
ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val);
if (ret) {
dev_err(data->dev, "Failed to read chip id register\n");
- goto out;
+ return ret;
}

if (val != BMA400_ID_REG_VAL) {
dev_err(data->dev, "Chip ID mismatch\n");
- ret = -ENODEV;
- goto out;
+ return -ENODEV;
}

data->regulators[BMA400_VDD_REGULATOR].supply = "vdd";
@@ -589,27 +608,31 @@ static int bma400_init(struct bma400_data *data)
"Failed to get regulators: %d\n",
ret);

- goto out;
+ return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (ret) {
dev_err(data->dev, "Failed to enable regulators: %d\n",
ret);
- goto out;
+ return ret;
}

+ ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data);
+ if (ret)
+ return ret;
+
ret = bma400_get_power_mode(data);
if (ret) {
dev_err(data->dev, "Failed to get the initial power-mode\n");
- goto err_reg_disable;
+ return ret;
}

if (data->power_mode != POWER_MODE_NORMAL) {
ret = bma400_set_power_mode(data, POWER_MODE_NORMAL);
if (ret) {
dev_err(data->dev, "Failed to wake up the device\n");
- goto err_reg_disable;
+ return ret;
}
/*
* TODO: The datasheet waits 1500us here in the example, but
@@ -618,19 +641,23 @@ static int bma400_init(struct bma400_data *data)
usleep_range(1500, 2000);
}

+ ret = devm_add_action_or_reset(data->dev, bma400_power_disable, data);
+ if (ret)
+ return ret;
+
bma400_init_tables();

ret = bma400_get_accel_output_data_rate(data);
if (ret)
- goto err_reg_disable;
+ return ret;

ret = bma400_get_accel_oversampling_ratio(data);
if (ret)
- goto err_reg_disable;
+ return ret;

ret = bma400_get_accel_scale(data);
if (ret)
- goto err_reg_disable;
+ return ret;

/*
* Once the interrupt engine is supported we might use the
@@ -639,12 +666,6 @@ static int bma400_init(struct bma400_data *data)
* channel.
*/
return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00);
-
-err_reg_disable:
- regulator_bulk_disable(ARRAY_SIZE(data->regulators),
- data->regulators);
-out:
- return ret;
}

static int bma400_read_raw(struct iio_dev *indio_dev,
@@ -822,32 +843,10 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
indio_dev->num_channels = ARRAY_SIZE(bma400_channels);
indio_dev->modes = INDIO_DIRECT_MODE;

- dev_set_drvdata(dev, indio_dev);
-
- return iio_device_register(indio_dev);
+ return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);

-void bma400_remove(struct device *dev)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct bma400_data *data = iio_priv(indio_dev);
- int ret;
-
- mutex_lock(&data->mutex);
- ret = bma400_set_power_mode(data, POWER_MODE_SLEEP);
- mutex_unlock(&data->mutex);
-
- if (ret)
- dev_warn(dev, "Failed to put device into sleep mode (%pe)\n", ERR_PTR(ret));
-
- regulator_bulk_disable(ARRAY_SIZE(data->regulators),
- data->regulators);
-
- iio_device_unregister(indio_dev);
-}
-EXPORT_SYMBOL_NS(bma400_remove, IIO_BMA400);
-
MODULE_AUTHOR("Dan Robertson <[email protected]>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c
index da104ffd3fe0..4f6e01a3b3a1 100644
--- a/drivers/iio/accel/bma400_i2c.c
+++ b/drivers/iio/accel/bma400_i2c.c
@@ -27,13 +27,6 @@ static int bma400_i2c_probe(struct i2c_client *client,
return bma400_probe(&client->dev, regmap, id->name);
}

-static int bma400_i2c_remove(struct i2c_client *client)
-{
- bma400_remove(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id bma400_i2c_ids[] = {
{ "bma400", 0 },
{ }
@@ -52,7 +45,6 @@ static struct i2c_driver bma400_i2c_driver = {
.of_match_table = bma400_of_i2c_match,
},
.probe = bma400_i2c_probe,
- .remove = bma400_i2c_remove,
.id_table = bma400_i2c_ids,
};

diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c
index 51f23bdc0ea5..28e240400a3f 100644
--- a/drivers/iio/accel/bma400_spi.c
+++ b/drivers/iio/accel/bma400_spi.c
@@ -87,11 +87,6 @@ static int bma400_spi_probe(struct spi_device *spi)
return bma400_probe(&spi->dev, regmap, id->name);
}

-static void bma400_spi_remove(struct spi_device *spi)
-{
- bma400_remove(&spi->dev);
-}
-
static const struct spi_device_id bma400_spi_ids[] = {
{ "bma400", 0 },
{ }
@@ -110,7 +105,6 @@ static struct spi_driver bma400_spi_driver = {
.of_match_table = bma400_of_spi_match,
},
.probe = bma400_spi_probe,
- .remove = bma400_spi_remove,
.id_table = bma400_spi_ids,
};

--
2.17.1


2022-05-09 09:25:51

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 01/10] iio: accel: bma400: Fix the scale min and max macro values

Changing the scale macro values to match the bma400 sensitivity
for 1 LSB of all the available ranges.

Fixes: 465c811f1f20 ("iio: accel: Add driver for the BMA400")
Signed-off-by: Jagath Jog J <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
---
drivers/iio/accel/bma400.h | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index c4c8d74155c2..80330c7ce17f 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -83,8 +83,27 @@
#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25
#define BMA400_ACC_ODR_MIN_HZ 12

-#define BMA400_SCALE_MIN 38357
-#define BMA400_SCALE_MAX 306864
+/*
+ * BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
+ * converting to micro values for +-2g range.
+ *
+ * For +-2g - 1 LSB = 0.976562 milli g = 0.009576 m/s^2
+ * For +-4g - 1 LSB = 1.953125 milli g = 0.019153 m/s^2
+ * For +-16g - 1 LSB = 7.8125 milli g = 0.076614 m/s^2
+ *
+ * The raw value which is used to select the different ranges is determined
+ * by the first bit set position from the scale value, so BMA400_SCALE_MIN
+ * should be odd.
+ *
+ * Scale values for +-2g, +-4g, +-8g and +-16g are populated into bma400_scales
+ * array by left shifting BMA400_SCALE_MIN.
+ * e.g.:
+ * To select +-2g = 9577 << 0 = raw value to write is 0.
+ * To select +-8g = 9577 << 2 = raw value to write is 2.
+ * To select +-16g = 9577 << 3 = raw value to write is 3.
+ */
+#define BMA400_SCALE_MIN 9577
+#define BMA400_SCALE_MAX 76617

#define BMA400_NUM_REGULATORS 2
#define BMA400_VDD_REGULATOR 0
--
2.17.1


2022-05-09 09:52:17

by Jagath Jog J

[permalink] [raw]
Subject: [PATCH v5 10/10] iio: accel: bma400: Add support for single and double tap events

Add support for single and double tap events based on the tap threshold
value and minimum quite time value between the taps. INT1 pin is used to
interrupt and event is pushed to userspace.

Signed-off-by: Jagath Jog J <[email protected]>
---
drivers/iio/accel/bma400.h | 11 ++
drivers/iio/accel/bma400_core.c | 176 ++++++++++++++++++++++++++++++++
2 files changed, 187 insertions(+)

diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index e8f802a82300..7331474433fa 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -40,6 +40,7 @@
#define BMA400_INT_STAT1_REG 0x0f
#define BMA400_INT_STAT2_REG 0x10
#define BMA400_INT12_MAP_REG 0x23
+#define BMA400_INT_ENG_OVRUN_MSK BIT(4)

/* Temperature register */
#define BMA400_TEMP_DATA_REG 0x11
@@ -105,6 +106,16 @@
#define BMA400_INT_GEN2_MSK BIT(3)
#define BMA400_GEN_HYST_MSK GENMASK(1, 0)

+/* TAP config registers */
+#define BMA400_TAP_CONFIG 0x57
+#define BMA400_TAP_CONFIG1 0x58
+#define BMA400_S_TAP_MSK BIT(2)
+#define BMA400_D_TAP_MSK BIT(3)
+#define BMA400_INT_S_TAP_MSK BIT(10)
+#define BMA400_INT_D_TAP_MSK BIT(11)
+#define BMA400_TAP_SEN_MSK GENMASK(2, 0)
+#define BMA400_TAP_QUITE_MSK GENMASK(3, 2)
+
/*
* BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
* converting to micro values for +-2g range.
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 8faff72625b3..9d19a0afd683 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -88,6 +88,7 @@ struct bma400_data {
bool step_event_en;
bool activity_event_en;
unsigned int generic_event_en;
+ unsigned int tap_event_en;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -187,6 +188,14 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
{ }
};

+static const struct iio_event_spec bma400_tap_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD),
+};
+
static const struct iio_event_spec bma400_step_detect_event = {
.type = IIO_EV_TYPE_CHANGE,
.dir = IIO_EV_DIR_NONE,
@@ -251,6 +260,19 @@ static const struct iio_event_spec bma400_accel_event[] = {
.num_event_specs = 1, \
}

+/*
+ * Single Tap and Double Tap events needs to be captured instantly, so only
+ * events are being configured.
+ */
+#define BMA400_TAP_CHANNEL(_chan2) { \
+ .type = IIO_TAP, \
+ .modified = 1, \
+ .channel2 = _chan2, \
+ .scan_index = -1, /* No buffer support */ \
+ .event_spec = &bma400_tap_event, \
+ .num_event_specs = 1, \
+}
+
static const struct iio_chan_spec bma400_channels[] = {
BMA400_ACC_CHANNEL(0, X),
BMA400_ACC_CHANNEL(1, Y),
@@ -278,6 +300,8 @@ static const struct iio_chan_spec bma400_channels[] = {
BMA400_ACTIVITY_CHANNEL(IIO_MOD_STILL),
BMA400_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
BMA400_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
+ BMA400_TAP_CHANNEL(IIO_MOD_TAP_SINGLE),
+ BMA400_TAP_CHANNEL(IIO_MOD_TAP_DOUBLE),
IIO_CHAN_SOFT_TIMESTAMP(4),
};

@@ -407,6 +431,14 @@ static int bma400_set_accel_output_data_rate(struct bma400_data *data,
unsigned int val;
int ret;

+ /*
+ * No need to change ODR when tap event is enabled because
+ * tap interrupt is operating with the data rate of 200Hz.
+ * See datasheet page 124.
+ */
+ if (data->tap_event_en)
+ return -EBUSY;
+
if (hz >= BMA400_ACC_ODR_MIN_WHOLE_HZ) {
if (uhz || hz > BMA400_ACC_ODR_MAX_HZ)
return -EINVAL;
@@ -1019,6 +1051,15 @@ static int bma400_read_event_config(struct iio_dev *indio_dev,
return data->step_event_en;
case IIO_ACTIVITY:
return data->activity_event_en;
+ case IIO_TAP:
+ switch (chan->channel2) {
+ case IIO_MOD_TAP_SINGLE:
+ return FIELD_GET(BMA400_S_TAP_MSK, data->tap_event_en);
+ case IIO_MOD_TAP_DOUBLE:
+ return FIELD_GET(BMA400_D_TAP_MSK, data->tap_event_en);
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1101,6 +1142,74 @@ static int bma400_activity_event_en(struct bma400_data *data,
return 0;
}

+static int bma400_tap_event_enable(struct bma400_data *data,
+ enum iio_modifier mod, int state)
+{
+ int ret;
+ unsigned int mask, field_value;
+
+ if (data->power_mode == POWER_MODE_SLEEP)
+ return -EBUSY;
+
+ /*
+ * acc_filt1 is the data source for the tap interrupt and it is
+ * operating on an input data rate of 200Hz.
+ */
+ if (!data->tap_event_en) {
+ ret = bma400_set_accel_output_data_rate(data, 200, 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG,
+ BMA400_S_TAP_MSK,
+ FIELD_PREP(BMA400_S_TAP_MSK, state));
+ if (ret)
+ return ret;
+
+ switch (mod) {
+ case IIO_MOD_TAP_SINGLE:
+ mask = BMA400_S_TAP_MSK;
+ set_mask_bits(&field_value, BMA400_S_TAP_MSK,
+ FIELD_PREP(BMA400_S_TAP_MSK, state));
+ break;
+ case IIO_MOD_TAP_DOUBLE:
+ mask = BMA400_D_TAP_MSK;
+ set_mask_bits(&field_value, BMA400_D_TAP_MSK,
+ FIELD_PREP(BMA400_D_TAP_MSK, state));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG, mask,
+ field_value);
+ if (ret)
+ return ret;
+
+ set_mask_bits(&data->tap_event_en, mask, field_value);
+ return 0;
+}
+
+static int bma400_disable_adv_interrupt(struct bma400_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, BMA400_INT_CONFIG0_REG, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, BMA400_INT_CONFIG1_REG, 0);
+ if (ret)
+ return ret;
+
+ data->tap_event_en = 0;
+ data->generic_event_en = 0;
+ data->step_event_en = 0;
+ data->activity_event_en = 0;
+ return 0;
+}
+
static int bma400_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@@ -1132,6 +1241,11 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
data->activity_event_en = state;
mutex_unlock(&data->mutex);
return 0;
+ case IIO_TAP:
+ mutex_lock(&data->mutex);
+ ret = bma400_tap_event_enable(data, chan->channel2, state);
+ mutex_unlock(&data->mutex);
+ return ret;
default:
return -EINVAL;
}
@@ -1196,6 +1310,23 @@ static int bma400_read_event_value(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_TAP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG, val);
+ if (ret)
+ return ret;
+ *val = FIELD_GET(BMA400_TAP_SEN_MSK, *val);
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, val);
+ if (ret)
+ return ret;
+ *val = FIELD_GET(BMA400_TAP_QUITE_MSK, *val);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1248,6 +1379,29 @@ static int bma400_write_event_value(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_TAP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 0 || val > 7)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap,
+ BMA400_TAP_CONFIG,
+ BMA400_TAP_SEN_MSK,
+ FIELD_PREP(BMA400_TAP_SEN_MSK,
+ val));
+ case IIO_EV_INFO_PERIOD:
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap,
+ BMA400_TAP_CONFIG1,
+ BMA400_TAP_QUITE_MSK,
+ FIELD_PREP(BMA400_TAP_QUITE_MSK,
+ val));
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1336,6 +1490,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
struct bma400_data *data = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
unsigned int act, ev_dir = IIO_EV_DIR_NONE;
+ unsigned int ev_mod = IIO_NO_MOD;
int ret;

/* Lock to protect the data->status */
@@ -1350,6 +1505,27 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
if (ret || !data->status)
goto unlock_err;

+ /* Disable all advance interrupts if interrupt engine overrun occurs */
+ if (FIELD_GET(BMA400_INT_ENG_OVRUN_MSK, le16_to_cpu(data->status))) {
+ bma400_disable_adv_interrupt(data);
+ dev_err(data->dev, "Interrupt engine overrun\n");
+ goto unlock_err;
+ }
+
+ if (FIELD_GET(BMA400_INT_S_TAP_MSK, le16_to_cpu(data->status)))
+ ev_mod = IIO_MOD_TAP_SINGLE;
+
+ if (FIELD_GET(BMA400_INT_D_TAP_MSK, le16_to_cpu(data->status)))
+ ev_mod = IIO_MOD_TAP_DOUBLE;
+
+ if (ev_mod != IIO_NO_MOD) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_TAP, 0,
+ ev_mod, IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_NONE),
+ timestamp);
+ }
+
if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status)))
ev_dir = IIO_EV_DIR_RISING;

--
2.17.1


2022-05-09 10:15:47

by Jagath Jog J

[permalink] [raw]
Subject: Re: [PATCH v5 09/10] iio: Add channel for tap and new modifiers for single and double tap

Hi Jonathan,

Thanks for accepting the patches.

On Sat, May 7, 2022 at 9:45 PM Jonathan Cameron <[email protected]> wrote:
>
> On Thu, 5 May 2022 19:00:20 +0530
> Jagath Jog J <[email protected]> wrote:
>
> Strangely the last time I can remember discussion around how to
> support tap detection was way back in the early days of IIO, perhaps
> 10 years ago. I don't recall us ever coming to a conclusion on how to do it.
>
> > Add new channel type for tap and also add new modifiers for single and
> > double tap. This channel and modifiers may be used by accelerometer
> > sensors to express single and double tap events. For directional tap,
> > modifiers like IIO_MOD_(X/Y/Z) can be used along with rising and
> > falling direction.
>
> Not sure how that would work seeing as there is only one modifier
> field and it's not a bitmap.
> The event code would need to encode both what type of tap and
> the direction and there aren't two fields in which to do that.
>
> One way I can see this 'might' work would be to use
> the event type to encode tap and the direction could be 'abused'
> to encode single vs double (or other events like this)
>
> in_accel_x_tap_single
> in_accel_x_tap_double
>
> We could possibly be more generic and have the 'type' as
> 'event' or something like that allowing us to use the
> 7 bit direction field to encode different detectable events
> (I'm not that keen on the name event though, could maybe
> map it to gesture which would cover some of the other
> motion pattern detection devices out there)
>
> That would give us
>
> in_accel_x_event_singletap
> in_accel_y_event_doubletap
>
> etc.
>
> How ever we move forwards we do it this want to be in a new series with a nice
> bold title to attract that attention of people who don't really
> care about he bma400 but do care about tap detection; it's
> a common feature of accelerometers.

Sure, I will try to make a new series for tap events with the given
inputs and send the RFC first to get all the comments from everyone.

>
> Jonathan
>
>
>
> >
> > Signed-off-by: Jagath Jog J <[email protected]>
>
>
> > ---
> > Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++
> > drivers/iio/industrialio-core.c | 3 +++
> > include/uapi/linux/iio/types.h | 3 +++
> > tools/iio/iio_event_monitor.c | 6 ++++++
> > 4 files changed, 23 insertions(+)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index d4ccc68fdcf0..bf2d10d6ad9b 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -2030,3 +2030,14 @@ Description:
> > Available range for the forced calibration value, expressed as:
> >
> > - a range specified as "[min step max]"
> > +
> > +What: /sys/.../events/in_tap_single_change_en
> > +What: /sys/.../events/in_tap_double_change_en
> > +KernelVersion: 5.19
> > +Contact: [email protected]
> > +Description:
> > + Accelerometer device detects single or double taps and generate
> > + events when threshold for minimum tap amplitide passes.
> > + E.g. a single tap event is generated when acceleration value
> > + crosses the minimum tap amplitude value set. Where tap threshold
> > + value is set by using in_tap_change_value.
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index e1ed44dec2ab..9b0d7bbd07fc 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = {
> > [IIO_POSITIONRELATIVE] = "positionrelative",
> > [IIO_PHASE] = "phase",
> > [IIO_MASSCONCENTRATION] = "massconcentration",
> > + [IIO_TAP] = "tap"
> > };
> >
> > static const char * const iio_modifier_names[] = {
> > @@ -134,6 +135,8 @@ static const char * const iio_modifier_names[] = {
> > [IIO_MOD_ETHANOL] = "ethanol",
> > [IIO_MOD_H2] = "h2",
> > [IIO_MOD_O2] = "o2",
> > + [IIO_MOD_TAP_SINGLE] = "single",
> > + [IIO_MOD_TAP_DOUBLE] = "double",
> > };
> >
> > /* relies on pairs of these shared then separate */
> > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> > index 472cead10d8d..d1e61c84e0d5 100644
> > --- a/include/uapi/linux/iio/types.h
> > +++ b/include/uapi/linux/iio/types.h
> > @@ -47,6 +47,7 @@ enum iio_chan_type {
> > IIO_POSITIONRELATIVE,
> > IIO_PHASE,
> > IIO_MASSCONCENTRATION,
> > + IIO_TAP,
> > };
> >
> > enum iio_modifier {
> > @@ -95,6 +96,8 @@ enum iio_modifier {
> > IIO_MOD_ETHANOL,
> > IIO_MOD_H2,
> > IIO_MOD_O2,
> > + IIO_MOD_TAP_SINGLE,
> > + IIO_MOD_TAP_DOUBLE,
> > };
> >
> > enum iio_event_type {
> > diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
> > index 2f4581658859..7fa7d4285f40 100644
> > --- a/tools/iio/iio_event_monitor.c
> > +++ b/tools/iio/iio_event_monitor.c
> > @@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = {
> > [IIO_POSITIONRELATIVE] = "positionrelative",
> > [IIO_PHASE] = "phase",
> > [IIO_MASSCONCENTRATION] = "massconcentration",
> > + [IIO_TAP] = "tap",
> > };
> >
> > static const char * const iio_ev_type_text[] = {
> > @@ -122,6 +123,8 @@ static const char * const iio_modifier_names[] = {
> > [IIO_MOD_PM4] = "pm4",
> > [IIO_MOD_PM10] = "pm10",
> > [IIO_MOD_O2] = "o2",
> > + [IIO_MOD_TAP_SINGLE] = "single",
> > + [IIO_MOD_TAP_DOUBLE] = "double",
> > };
> >
> > static bool event_is_known(struct iio_event_data *event)
> > @@ -164,6 +167,7 @@ static bool event_is_known(struct iio_event_data *event)
> > case IIO_POSITIONRELATIVE:
> > case IIO_PHASE:
> > case IIO_MASSCONCENTRATION:
> > + case IIO_TAP:
> > break;
> > default:
> > return false;
> > @@ -215,6 +219,8 @@ static bool event_is_known(struct iio_event_data *event)
> > case IIO_MOD_PM4:
> > case IIO_MOD_PM10:
> > case IIO_MOD_O2:
> > + case IIO_MOD_TAP_SINGLE:
> > + case IIO_MOD_TAP_DOUBLE:
> > break;
> > default:
> > return false;
>

2022-05-09 11:17:38

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v5 09/10] iio: Add channel for tap and new modifiers for single and double tap

On Thu, 5 May 2022 19:00:20 +0530
Jagath Jog J <[email protected]> wrote:

Strangely the last time I can remember discussion around how to
support tap detection was way back in the early days of IIO, perhaps
10 years ago. I don't recall us ever coming to a conclusion on how to do it.

> Add new channel type for tap and also add new modifiers for single and
> double tap. This channel and modifiers may be used by accelerometer
> sensors to express single and double tap events. For directional tap,
> modifiers like IIO_MOD_(X/Y/Z) can be used along with rising and
> falling direction.

Not sure how that would work seeing as there is only one modifier
field and it's not a bitmap.
The event code would need to encode both what type of tap and
the direction and there aren't two fields in which to do that.

One way I can see this 'might' work would be to use
the event type to encode tap and the direction could be 'abused'
to encode single vs double (or other events like this)

in_accel_x_tap_single
in_accel_x_tap_double

We could possibly be more generic and have the 'type' as
'event' or something like that allowing us to use the
7 bit direction field to encode different detectable events
(I'm not that keen on the name event though, could maybe
map it to gesture which would cover some of the other
motion pattern detection devices out there)

That would give us

in_accel_x_event_singletap
in_accel_y_event_doubletap

etc.

How ever we move forwards we do it this want to be in a new series with a nice
bold title to attract that attention of people who don't really
care about he bma400 but do care about tap detection; it's
a common feature of accelerometers.

Jonathan



>
> Signed-off-by: Jagath Jog J <[email protected]>


> ---
> Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++
> drivers/iio/industrialio-core.c | 3 +++
> include/uapi/linux/iio/types.h | 3 +++
> tools/iio/iio_event_monitor.c | 6 ++++++
> 4 files changed, 23 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index d4ccc68fdcf0..bf2d10d6ad9b 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -2030,3 +2030,14 @@ Description:
> Available range for the forced calibration value, expressed as:
>
> - a range specified as "[min step max]"
> +
> +What: /sys/.../events/in_tap_single_change_en
> +What: /sys/.../events/in_tap_double_change_en
> +KernelVersion: 5.19
> +Contact: [email protected]
> +Description:
> + Accelerometer device detects single or double taps and generate
> + events when threshold for minimum tap amplitide passes.
> + E.g. a single tap event is generated when acceleration value
> + crosses the minimum tap amplitude value set. Where tap threshold
> + value is set by using in_tap_change_value.
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index e1ed44dec2ab..9b0d7bbd07fc 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = {
> [IIO_POSITIONRELATIVE] = "positionrelative",
> [IIO_PHASE] = "phase",
> [IIO_MASSCONCENTRATION] = "massconcentration",
> + [IIO_TAP] = "tap"
> };
>
> static const char * const iio_modifier_names[] = {
> @@ -134,6 +135,8 @@ static const char * const iio_modifier_names[] = {
> [IIO_MOD_ETHANOL] = "ethanol",
> [IIO_MOD_H2] = "h2",
> [IIO_MOD_O2] = "o2",
> + [IIO_MOD_TAP_SINGLE] = "single",
> + [IIO_MOD_TAP_DOUBLE] = "double",
> };
>
> /* relies on pairs of these shared then separate */
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index 472cead10d8d..d1e61c84e0d5 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -47,6 +47,7 @@ enum iio_chan_type {
> IIO_POSITIONRELATIVE,
> IIO_PHASE,
> IIO_MASSCONCENTRATION,
> + IIO_TAP,
> };
>
> enum iio_modifier {
> @@ -95,6 +96,8 @@ enum iio_modifier {
> IIO_MOD_ETHANOL,
> IIO_MOD_H2,
> IIO_MOD_O2,
> + IIO_MOD_TAP_SINGLE,
> + IIO_MOD_TAP_DOUBLE,
> };
>
> enum iio_event_type {
> diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
> index 2f4581658859..7fa7d4285f40 100644
> --- a/tools/iio/iio_event_monitor.c
> +++ b/tools/iio/iio_event_monitor.c
> @@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = {
> [IIO_POSITIONRELATIVE] = "positionrelative",
> [IIO_PHASE] = "phase",
> [IIO_MASSCONCENTRATION] = "massconcentration",
> + [IIO_TAP] = "tap",
> };
>
> static const char * const iio_ev_type_text[] = {
> @@ -122,6 +123,8 @@ static const char * const iio_modifier_names[] = {
> [IIO_MOD_PM4] = "pm4",
> [IIO_MOD_PM10] = "pm10",
> [IIO_MOD_O2] = "o2",
> + [IIO_MOD_TAP_SINGLE] = "single",
> + [IIO_MOD_TAP_DOUBLE] = "double",
> };
>
> static bool event_is_known(struct iio_event_data *event)
> @@ -164,6 +167,7 @@ static bool event_is_known(struct iio_event_data *event)
> case IIO_POSITIONRELATIVE:
> case IIO_PHASE:
> case IIO_MASSCONCENTRATION:
> + case IIO_TAP:
> break;
> default:
> return false;
> @@ -215,6 +219,8 @@ static bool event_is_known(struct iio_event_data *event)
> case IIO_MOD_PM4:
> case IIO_MOD_PM10:
> case IIO_MOD_O2:
> + case IIO_MOD_TAP_SINGLE:
> + case IIO_MOD_TAP_DOUBLE:
> break;
> default:
> return false;


2022-05-09 11:31:41

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v5 08/10] iio: accel: bma400: Add support for activity and inactivity events

On Thu, 5 May 2022 19:00:19 +0530
Jagath Jog J <[email protected]> wrote:

> Add support for activity and inactivity events for all axis based on the
> threshold, duration and hysteresis value set from the userspace. INT1 pin
> is used to interrupt and event is pushed to userspace.
>
> Signed-off-by: Jagath Jog J <[email protected]>
I've applied the first 8 patches to the togreg branch of iio.git.

Most of the time it's a bad idea to introduce significant new features
in later versions of patch set as it means people mostly don't spot
that there is something new to look at.

That particular feature is also defining new and non trivial ABI.

I'll take a look at it and offer some initial comments, but to
get proper review it needs to be a new series with a clear rational
in a cover letter etc.

Jonathan

> ---
> drivers/iio/accel/bma400.h | 11 ++
> drivers/iio/accel/bma400_core.c | 232 +++++++++++++++++++++++++++++++-
> 2 files changed, 242 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
> index 0faa40fdbbf8..e8f802a82300 100644
> --- a/drivers/iio/accel/bma400.h
> +++ b/drivers/iio/accel/bma400.h
> @@ -94,6 +94,17 @@
> #define BMA400_ACC_ODR_MIN_WHOLE_HZ 25
> #define BMA400_ACC_ODR_MIN_HZ 12
>
> +/* Generic interrupts register */
> +#define BMA400_GEN1INT_CONFIG0 0x3f
> +#define BMA400_GEN2INT_CONFIG0 0x4A
> +#define BMA400_GEN_CONFIG1_OFF 0x01
> +#define BMA400_GEN_CONFIG2_OFF 0x02
> +#define BMA400_GEN_CONFIG3_OFF 0x03
> +#define BMA400_GEN_CONFIG31_OFF 0x04
> +#define BMA400_INT_GEN1_MSK BIT(2)
> +#define BMA400_INT_GEN2_MSK BIT(3)
> +#define BMA400_GEN_HYST_MSK GENMASK(1, 0)
> +
> /*
> * BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
> * converting to micro values for +-2g range.
> diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
> index 1e4923064b63..8faff72625b3 100644
> --- a/drivers/iio/accel/bma400_core.c
> +++ b/drivers/iio/accel/bma400_core.c
> @@ -87,6 +87,7 @@ struct bma400_data {
> int steps_enabled;
> bool step_event_en;
> bool activity_event_en;
> + unsigned int generic_event_en;
> /* Correct time stamp alignment */
> struct {
> __le16 buff[3];
> @@ -94,6 +95,7 @@ struct bma400_data {
> s64 ts __aligned(8);
> } buffer __aligned(IIO_ALIGN);
> __le16 status;
> + __be16 duration;
> };
>
> static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
> @@ -197,6 +199,25 @@ static const struct iio_event_spec bma400_activity_event = {
> .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
> };
>
> +static const struct iio_event_spec bma400_accel_event[] = {
> + {
> + .type = IIO_EV_TYPE_MAG,
> + .dir = IIO_EV_DIR_FALLING,
> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
> + BIT(IIO_EV_INFO_PERIOD) |
> + BIT(IIO_EV_INFO_HYSTERESIS) |
> + BIT(IIO_EV_INFO_ENABLE),
> + },
> + {
> + .type = IIO_EV_TYPE_MAG,
> + .dir = IIO_EV_DIR_RISING,
> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
> + BIT(IIO_EV_INFO_PERIOD) |
> + BIT(IIO_EV_INFO_HYSTERESIS) |
> + BIT(IIO_EV_INFO_ENABLE),
> + },
> +};
> +
> #define BMA400_ACC_CHANNEL(_index, _axis) { \
> .type = IIO_ACCEL, \
> .modified = 1, \
> @@ -216,6 +237,8 @@ static const struct iio_event_spec bma400_activity_event = {
> .storagebits = 16, \
> .endianness = IIO_LE, \
> }, \
> + .event_spec = bma400_accel_event, \
> + .num_event_specs = ARRAY_SIZE(bma400_accel_event) \
> }
>
> #define BMA400_ACTIVITY_CHANNEL(_chan2) { \
> @@ -981,6 +1004,17 @@ static int bma400_read_event_config(struct iio_dev *indio_dev,
> struct bma400_data *data = iio_priv(indio_dev);
>
> switch (chan->type) {
> + case IIO_ACCEL:
> + switch (dir) {
> + case IIO_EV_DIR_RISING:
> + return FIELD_GET(BMA400_INT_GEN1_MSK,
> + data->generic_event_en);
> + case IIO_EV_DIR_FALLING:
> + return FIELD_GET(BMA400_INT_GEN2_MSK,
> + data->generic_event_en);
> + default:
> + return -EINVAL;
> + }
> case IIO_STEPS:
> return data->step_event_en;
> case IIO_ACTIVITY:
> @@ -1008,6 +1042,65 @@ static int bma400_steps_event_enable(struct bma400_data *data, int state)
> return 0;
> }
>
> +static int bma400_activity_event_en(struct bma400_data *data,
> + enum iio_event_direction dir,
> + int state)
> +{
> + int ret, reg, msk, value, field_value;
> +
> + switch (dir) {
> + case IIO_EV_DIR_RISING:
> + reg = BMA400_GEN1INT_CONFIG0;
> + msk = BMA400_INT_GEN1_MSK;
> + value = 2;
> + set_mask_bits(&field_value, BMA400_INT_GEN1_MSK,
> + FIELD_PREP(BMA400_INT_GEN1_MSK, state));
> + break;
> + case IIO_EV_DIR_FALLING:
> + reg = BMA400_GEN2INT_CONFIG0;
> + msk = BMA400_INT_GEN2_MSK;
> + value = 0;
> + set_mask_bits(&field_value, BMA400_INT_GEN2_MSK,
> + FIELD_PREP(BMA400_INT_GEN2_MSK, state));
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* Enabling all axis for interrupt evaluation */
> + ret = regmap_write(data->regmap, reg, 0xF8);
> + if (ret)
> + return ret;
> +
> + /* OR combination of all axis for interrupt evaluation */
> + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG1_OFF, value);
> + if (ret)
> + return ret;
> +
> + /* Initial value to avoid interrupts while enabling*/
> + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG2_OFF, 0x0A);
> + if (ret)
> + return ret;
> +
> + /* Initial duration value to avoid interrupts while enabling*/
> + ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG31_OFF, 0x0F);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG, msk,
> + field_value);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG, msk,
> + field_value);
> + if (ret)
> + return ret;
> +
> + set_mask_bits(&data->generic_event_en, msk, field_value);
> + return 0;
> +}
> +
> static int bma400_write_event_config(struct iio_dev *indio_dev,
> const struct iio_chan_spec *chan,
> enum iio_event_type type,
> @@ -1017,6 +1110,11 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
> int ret;
>
> switch (chan->type) {
> + case IIO_ACCEL:
> + mutex_lock(&data->mutex);
> + ret = bma400_activity_event_en(data, dir, state);
> + mutex_unlock(&data->mutex);
> + return ret;
> case IIO_STEPS:
> mutex_lock(&data->mutex);
> ret = bma400_steps_event_enable(data, state);
> @@ -1039,6 +1137,122 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
> }
> }
>
> +static int get_gen_config_reg(enum iio_event_direction dir)
> +{
> + switch (dir) {
> + case IIO_EV_DIR_FALLING:
> + return BMA400_GEN2INT_CONFIG0;
> + case IIO_EV_DIR_RISING:
> + return BMA400_GEN1INT_CONFIG0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int bma400_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 bma400_data *data = iio_priv(indio_dev);
> + int ret, reg;
> +
> + switch (chan->type) {
> + case IIO_ACCEL:
> + reg = get_gen_config_reg(dir);
> + if (reg < 0)
> + return -EINVAL;
> +
> + *val2 = 0;
> + switch (info) {
> + case IIO_EV_INFO_VALUE:
> + ret = regmap_read(data->regmap,
> + reg + BMA400_GEN_CONFIG2_OFF,
> + val);
> + if (ret)
> + return ret;
> + return IIO_VAL_INT;
> + case IIO_EV_INFO_PERIOD:
> + mutex_lock(&data->mutex);
> + ret = regmap_bulk_read(data->regmap,
> + reg + BMA400_GEN_CONFIG3_OFF,
> + &data->duration,
> + sizeof(data->duration));
> + if (ret) {
> + mutex_unlock(&data->mutex);
> + return ret;
> + }
> + *val = be16_to_cpu(data->duration);
> + mutex_unlock(&data->mutex);
> + return IIO_VAL_INT;
> + case IIO_EV_INFO_HYSTERESIS:
> + ret = regmap_read(data->regmap, reg, val);
> + if (ret)
> + return ret;
> + *val = FIELD_GET(BMA400_GEN_HYST_MSK, *val);
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int bma400_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 bma400_data *data = iio_priv(indio_dev);
> + int reg, ret;
> +
> + switch (chan->type) {
> + case IIO_ACCEL:
> + reg = get_gen_config_reg(dir);
> + if (reg < 0)
> + return -EINVAL;
> +
> + switch (info) {
> + case IIO_EV_INFO_VALUE:
> + if (val < 1 || val > 255)
> + return -EINVAL;
> +
> + return regmap_write(data->regmap,
> + reg + BMA400_GEN_CONFIG2_OFF,
> + val);
> + case IIO_EV_INFO_PERIOD:
> + if (val < 1 || val > 65535)
> + return -EINVAL;
> +
> + mutex_lock(&data->mutex);
> + put_unaligned_be16(val, &data->duration);
> + ret = regmap_bulk_write(data->regmap,
> + reg + BMA400_GEN_CONFIG3_OFF,
> + &data->duration,
> + sizeof(data->duration));
> + mutex_unlock(&data->mutex);
> + return ret;
> + case IIO_EV_INFO_HYSTERESIS:
> + if (val < 0 || val > 3)
> + return -EINVAL;
> +
> + return regmap_update_bits(data->regmap, reg,
> + BMA400_GEN_HYST_MSK,
> + FIELD_PREP(BMA400_GEN_HYST_MSK,
> + val));
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
> bool state)
> {
> @@ -1071,6 +1285,8 @@ static const struct iio_info bma400_info = {
> .write_raw_get_fmt = bma400_write_raw_get_fmt,
> .read_event_config = bma400_read_event_config,
> .write_event_config = bma400_write_event_config,
> + .write_event_value = bma400_write_event_value,
> + .read_event_value = bma400_read_event_value,
> };
>
> static const struct iio_trigger_ops bma400_trigger_ops = {
> @@ -1119,7 +1335,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
> struct iio_dev *indio_dev = private;
> struct bma400_data *data = iio_priv(indio_dev);
> s64 timestamp = iio_get_time_ns(indio_dev);
> - unsigned int act;
> + unsigned int act, ev_dir = IIO_EV_DIR_NONE;
> int ret;
>
> /* Lock to protect the data->status */
> @@ -1134,6 +1350,20 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
> if (ret || !data->status)
> goto unlock_err;
>
> + if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status)))
> + ev_dir = IIO_EV_DIR_RISING;
> +
> + if (FIELD_GET(BMA400_INT_GEN2_MSK, le16_to_cpu(data->status)))
> + 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_MAG, ev_dir),
> + timestamp);
> + }
> +
> if (FIELD_GET(BMA400_STEP_STAT_MASK, le16_to_cpu(data->status))) {
> iio_push_event(indio_dev,
> IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,