/* single/double tap */
enum adxl345_tap_type {
ADXL345_SINGLE_TAP,
+ ADXL345_DOUBLE_TAP,
};
static const unsigned int adxl345_tap_int_reg[] = {
[ADXL345_SINGLE_TAP] = ADXL345_INT_SINGLE_TAP,
+ [ADXL345_DOUBLE_TAP] = ADXL345_INT_DOUBLE_TAP,
};
enum adxl345_tap_time_type {
+ ADXL345_TAP_TIME_LATENT,
+ ADXL345_TAP_TIME_WINDOW,
ADXL345_TAP_TIME_DUR,
};
static const unsigned int adxl345_tap_time_reg[] = {
+ [ADXL345_TAP_TIME_LATENT] = ADXL345_REG_LATENT,
+ [ADXL345_TAP_TIME_WINDOW] = ADXL345_REG_WINDOW,
[ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR,
};
u8 fifo_mode;
u32 tap_duration_us;
+ u32 tap_latent_us;
+ u32 tap_window_us;
__le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN);
};
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_TIMEOUT),
},
+ {
+ /* double tap */
+ .type = IIO_EV_TYPE_GESTURE,
+ .dir = IIO_EV_DIR_DOUBLETAP,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_RESET_TIMEOUT) |
+ BIT(IIO_EV_INFO_TAP2_MIN_DELAY),
+ },
};
#define ADXL345_CHANNEL(index, reg, axis) { \
unsigned int tap_threshold;
bool axis_valid;
bool singletap_args_valid = false;
+ bool doubletap_args_valid = false;
bool en = false;
u32 axis_ctrl;
int ret;
*/
singletap_args_valid = tap_threshold > 0 && st->tap_duration_us > 0;
- if (type == ADXL345_SINGLE_TAP)
+ if (type == ADXL345_SINGLE_TAP) {
en = axis_valid && singletap_args_valid;
+ } else {
+ /* doubletap: Window must be equal or greater than latent! */
+ doubletap_args_valid = st->tap_latent_us > 0 &&
+ st->tap_window_us > 0 &&
+ st->tap_window_us >= st->tap_latent_us;
+
+ en = axis_valid && singletap_args_valid && doubletap_args_valid;
+ }
if (state && en)
int_map |= adxl345_tap_int_reg[type];
return _adxl345_set_tap_int(st, ADXL345_SINGLE_TAP, en);
}
+static int adxl345_set_doubletap_en(struct adxl345_state *st, bool en)
+{
+ return _adxl345_set_tap_int(st, ADXL345_DOUBLE_TAP, en);
+}
+
static int _adxl345_set_tap_time(struct adxl345_state *st,
enum adxl345_tap_time_type type, u32 val_us)
{
unsigned int regval;
switch (type) {
+ case ADXL345_TAP_TIME_WINDOW:
+ st->tap_window_us = val_us;
+ break;
+ case ADXL345_TAP_TIME_LATENT:
+ st->tap_latent_us = val_us;
+ break;
case ADXL345_TAP_TIME_DUR:
st->tap_duration_us = val_us;
break;
return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_DUR, val_fract_us);
}
+static int adxl345_set_tap_window(struct adxl345_state *st, u32 val_int,
+ u32 val_fract_us)
+{
+ /*
+ * Max value is 255 * 1250 us = 0.318750 seconds
+ *
+ * Note: the scaling is similar to the scaling in the ADXL380
+ */
+ if (val_int || val_fract_us > 318750)
+ return -EINVAL;
+
+ return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_WINDOW, val_fract_us);
+}
+
+static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int,
+ u32 val_fract_us)
+{
+ /*
+ * Max value is 255 * 1250 us = 0.318750 seconds
+ *
+ * Note: the scaling is similar to the scaling in the ADXL380
+ */
+ if (val_int || val_fract_us > 318750)
+ return -EINVAL;
+
+ return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us);
+}
+
static int adxl345_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
if (ret)
return ret;
return int_en;
+ case IIO_EV_DIR_DOUBLETAP:
+ ret = adxl345_is_tap_en(st, chan->channel2,
+ ADXL345_DOUBLE_TAP, &int_en);
+ if (ret)
+ return ret;
+ return int_en;
default:
return -EINVAL;
}
switch (dir) {
case IIO_EV_DIR_SINGLETAP:
return adxl345_set_singletap_en(st, chan->channel2, state);
+ case IIO_EV_DIR_DOUBLETAP:
+ return adxl345_set_doubletap_en(st, state);
default:
return -EINVAL;
}
*val = st->tap_duration_us;
*val2 = 1000000;
return IIO_VAL_FRACTIONAL;
+ case IIO_EV_INFO_RESET_TIMEOUT:
+ *val = st->tap_window_us;
+ *val2 = 1000000;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_EV_INFO_TAP2_MIN_DELAY:
+ *val = st->tap_latent_us;
+ *val2 = 1000000;
+ return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
if (ret)
return ret;
break;
+ case IIO_EV_INFO_RESET_TIMEOUT:
+ ret = adxl345_set_tap_window(st, val, val2);
+ if (ret)
+ return ret;
+ break;
+ case IIO_EV_INFO_TAP2_MIN_DELAY:
+ ret = adxl345_set_tap_latent(st, val, val2);
+ if (ret)
+ return ret;
+ break;
default:
return -EINVAL;
}
return ret;
}
+ if (FIELD_GET(ADXL345_INT_DOUBLE_TAP, int_stat)) {
+ ret = iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, tap_dir,
+ IIO_EV_TYPE_GESTURE,
+ IIO_EV_DIR_DOUBLETAP),
+ ts);
+ if (ret)
+ return ret;
+ }
+
if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) {
samples = adxl345_get_samples(st);
if (samples < 0)
/* Init with reasonable values */
tap_threshold = 48; /* 48 [0x30] -> ~3g */
st->tap_duration_us = 16; /* 16 [0x10] -> .010 */
+ st->tap_window_us = 64; /* 64 [0x40] -> .080 */
+ st->tap_latent_us = 16; /* 16 [0x10] -> .020 */
indio_dev->name = st->info->name;
indio_dev->info = &adxl345_info;