Re: [PATCH 2/2] iio: mma8452: add freefall detection for Freescale's accelerometers
From: Jonathan Cameron
Date: Sat Dec 12 2015 - 10:34:47 EST
On 08/12/15 16:21, Martin Kepplinger wrote:
> This adds freefall event detection to the supported devices. It adds
> the in_accel_x&y&z_mag_falling_en iio event attribute, which activates
> freefall mode.
>
> In freefall mode, the current acceleration magnitude (AND combination
> of all axis values) is compared to the specified threshold.
> If it falls under the threshold (in_accel_mag_falling_value),
> the appropriate IIO event code is generated.
>
> The values of rising and falling versions of various sysfs files are
> shared, which is compliant to the IIO specification.
>
> This is what the sysfs "events" directory for these devices looks
> like after this change:
>
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_mag_falling_period
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_mag_falling_value
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_mag_rising_period
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_mag_rising_value
> -r--r--r-- 4096 Oct 23 08:45 in_accel_scale
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_x&y&z_mag_falling_en
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_x_mag_rising_en
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_y_mag_rising_en
> -rw-r--r-- 4096 Oct 23 08:45 in_accel_z_mag_rising_en
>
> Signed-off-by: Martin Kepplinger <martin.kepplinger@xxxxxxxxxxxxxxxxxxxxx>
> Signed-off-by: Christoph Muellner <christoph.muellner@xxxxxxxxxxxxxxxxxxxxx>
I think the read back of other events being enabled is broken by this.
Otherwise a few other minor bits and bobs.
Jonathan
> ---
> Other combinations (x&y, x&z or y&z) might be added later. This is the most
> useful for freefall detection.
>
>
> drivers/iio/accel/mma8452.c | 156 ++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 137 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
> index 162bbef..c8ac88c 100644
> --- a/drivers/iio/accel/mma8452.c
> +++ b/drivers/iio/accel/mma8452.c
> @@ -15,7 +15,7 @@
> *
> * 7-bit I2C slave address 0x1c/0x1d (pin selectable)
> *
> - * TODO: orientation / freefall events, autosleep
> + * TODO: orientation events, autosleep
> */
>
> #include <linux/module.h>
> @@ -143,6 +143,14 @@ struct mma_chip_info {
> u8 ev_count;
> };
>
> +enum {
> + idx_x,
> + idx_y,
> + idx_z,
> + idx_ts,
> + idx_xyz,
> +};
I would have slightly prefered the change to this enum to have been
done as a precursor patch as it would have lead to an easily checked
step 1 followed by the interesting stuff in step 2.
> +
> static int mma8452_drdy(struct mma8452_data *data)
> {
> int tries = 150;
> @@ -409,6 +417,46 @@ fail:
> return ret;
> }
>
> +static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
> +{
> + int val;
> + const struct mma_chip_info *chip = data->chip_info;
> +
> + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
> + if (val < 0)
> + return val;
> +
> + return !(val & MMA8452_FF_MT_CFG_OAE);
> +}
> +
> +static int mma8452_set_freefall_mode(struct mma8452_data *data, u8 state)
> +{
> + int val, ret;
> + const struct mma_chip_info *chip = data->chip_info;
> +
> + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
> + if (val < 0)
> + return val;
> +
> + if (state && !(mma8452_freefall_mode_enabled(data))) {
> + val |= BIT(idx_x + chip->ev_cfg_chan_shift);
> + val |= BIT(idx_y + chip->ev_cfg_chan_shift);
> + val |= BIT(idx_z + chip->ev_cfg_chan_shift);
> + val &= ~MMA8452_FF_MT_CFG_OAE;
> + } else if (!state && mma8452_freefall_mode_enabled(data)) {
> + val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
> + val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
> + val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
> + val |= MMA8452_FF_MT_CFG_OAE;
> + }
> +
> + ret = mma8452_change_config(data, chip->ev_cfg, val);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
> int val, int val2)
> {
> @@ -602,6 +650,11 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
> const struct mma_chip_info *chip = data->chip_info;
> int ret;
>
> + switch (chan->channel2) {
> + case IIO_MOD_X_AND_Y_AND_Z:
> + return mma8452_freefall_mode_enabled(data);
> + }
> +
> ret = i2c_smbus_read_byte_data(data->client,
> data->chip_info->ev_cfg);
> if (ret < 0)
I may have missed something here, but I think the check for the other events
being enabled needs updating as well. We are using the same bits in the
register afterall.
> @@ -620,6 +673,11 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
> const struct mma_chip_info *chip = data->chip_info;
> int val;
>
> + switch (chan->channel2) {
> + case IIO_MOD_X_AND_Y_AND_Z:
> + return mma8452_set_freefall_mode(data, state);
> + }
> +
> val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
> if (val < 0)
> return val;
> @@ -630,7 +688,6 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
> val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
>
> val |= chip->ev_cfg_ele;
> - val |= MMA8452_FF_MT_CFG_OAE;
>
> return mma8452_change_config(data, chip->ev_cfg, val);
> }
> @@ -639,12 +696,26 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
> {
> struct mma8452_data *data = iio_priv(indio_dev);
> s64 ts = iio_get_time_ns();
> - int src;
> + int src, cfg;
>
> src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
> if (src < 0)
> return;
>
> + cfg = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_cfg);
> + if (cfg < 0)
> + return;
This strikes me as odd. I'd assume ev_cfg could be cached and avoid the
read here? It's basically saying if we enable a free fall event all
the individual ones are disabled...
> +
> + if (!(cfg & MMA8452_FF_MT_CFG_OAE)) {
> + iio_push_event(indio_dev,
> + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
> + IIO_MOD_X_AND_Y_AND_Z,
> + IIO_EV_TYPE_MAG,
> + IIO_EV_DIR_FALLING),
> + ts);
> + return;
> + }
> +
> if (src & data->chip_info->ev_src_xe)
> iio_push_event(indio_dev,
> IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
> @@ -738,6 +809,27 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
> return 0;
> }
>
> +static const struct iio_event_spec mma8452_freefall_event[] = {
> + {
> + .type = IIO_EV_TYPE_MAG,
> + .dir = IIO_EV_DIR_FALLING,
> + .mask_separate = BIT(IIO_EV_INFO_ENABLE),
> + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
> + BIT(IIO_EV_INFO_PERIOD) |
> + BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
> + },
> +};
> +
> +static const struct iio_event_spec mma8652_freefall_event[] = {
> + {
> + .type = IIO_EV_TYPE_MAG,
> + .dir = IIO_EV_DIR_FALLING,
> + .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 mma8452_transient_event[] = {
> {
> .type = IIO_EV_TYPE_MAG,
> @@ -774,6 +866,24 @@ static struct attribute_group mma8452_event_attribute_group = {
> .attrs = mma8452_event_attributes,
> };
>
> +#define MMA8452_FREEFALL_CHANNEL(modifier, idx) { \
> + .type = IIO_ACCEL, \
> + .modified = 1, \
> + .channel2 = modifier, \
> + .scan_index = idx, \
> + .event_spec = mma8452_freefall_event, \
> + .num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
> +}
> +
> +#define MMA8652_FREEFALL_CHANNEL(modifier, idx) { \
> + .type = IIO_ACCEL, \
> + .modified = 1, \
> + .channel2 = modifier, \
> + .scan_index = idx, \
This isn't a 'real' channel with data to push into the buffer,
so I'd expect to see an scan_index of -1 to stop it appearing
under there. Same for the other varients.
> + .event_spec = mma8652_freefall_event, \
> + .num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
> +}
> +
> #define MMA8452_CHANNEL(axis, idx, bits) { \
> .type = IIO_ACCEL, \
> .modified = 1, \
> @@ -816,31 +926,35 @@ static struct attribute_group mma8452_event_attribute_group = {
> }
>
> static const struct iio_chan_spec mma8452_channels[] = {
> - MMA8452_CHANNEL(X, 0, 12),
> - MMA8452_CHANNEL(Y, 1, 12),
> - MMA8452_CHANNEL(Z, 2, 12),
> - IIO_CHAN_SOFT_TIMESTAMP(3),
> + MMA8452_CHANNEL(X, idx_x, 12),
> + MMA8452_CHANNEL(Y, idx_y, 12),
> + MMA8452_CHANNEL(Z, idx_z, 12),
> + IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
> + MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z, idx_xyz),
> };
>
> static const struct iio_chan_spec mma8453_channels[] = {
> - MMA8452_CHANNEL(X, 0, 10),
> - MMA8452_CHANNEL(Y, 1, 10),
> - MMA8452_CHANNEL(Z, 2, 10),
> - IIO_CHAN_SOFT_TIMESTAMP(3),
> + MMA8452_CHANNEL(X, idx_x, 10),
> + MMA8452_CHANNEL(Y, idx_y, 10),
> + MMA8452_CHANNEL(Z, idx_z, 10),
> + IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
> + MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z, idx_xyz),
> };
>
> static const struct iio_chan_spec mma8652_channels[] = {
> - MMA8652_CHANNEL(X, 0, 12),
> - MMA8652_CHANNEL(Y, 1, 12),
> - MMA8652_CHANNEL(Z, 2, 12),
> - IIO_CHAN_SOFT_TIMESTAMP(3),
> + MMA8652_CHANNEL(X, idx_x, 12),
> + MMA8652_CHANNEL(Y, idx_y, 12),
> + MMA8652_CHANNEL(Z, idx_z, 12),
> + IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
> + MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z, idx_xyz),
> };
>
> static const struct iio_chan_spec mma8653_channels[] = {
> - MMA8652_CHANNEL(X, 0, 10),
> - MMA8652_CHANNEL(Y, 1, 10),
> - MMA8652_CHANNEL(Z, 2, 10),
> - IIO_CHAN_SOFT_TIMESTAMP(3),
> + MMA8652_CHANNEL(X, idx_x, 10),
> + MMA8652_CHANNEL(Y, idx_y, 10),
> + MMA8652_CHANNEL(Z, idx_z, 10),
> + IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
> + MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z, idx_xyz),
> };
>
> enum {
> @@ -1183,6 +1297,10 @@ static int mma8452_probe(struct i2c_client *client,
> if (ret < 0)
> goto buffer_cleanup;
>
> + ret = mma8452_set_freefall_mode(data, 0);
> + if (ret)
> + return ret;
> +
> return 0;
>
> buffer_cleanup:
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/