summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/bma400.h11
-rw-r--r--drivers/iio/accel/bma400_core.c232
2 files changed, 242 insertions, 1 deletions
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 e9773266fd27..517920400df1 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,