Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: accel: adxl345: add double tap feature

Add the double tap feature of the sensor. The interrupt handler needs
to catch and forward the event to the IIO channel. The single tap
implementation now is extended to deal with double tap as well.

Doubletap introduces window and latency times, both in us. Since both
times are scaled, the 8-bit register value is stored in hardware,
where the scaled value in [us] is stored as member variable.

Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
Link: https://patch.msgid.link/20250414184245.100280-4-l.rubusch@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Lothar Rubusch and committed by
Jonathan Cameron
5b307f5a 7478933f

+103 -1
+103 -1
drivers/iio/accel/adxl345_core.c
··· 42 42 /* single/double tap */ 43 43 enum adxl345_tap_type { 44 44 ADXL345_SINGLE_TAP, 45 + ADXL345_DOUBLE_TAP, 45 46 }; 46 47 47 48 static const unsigned int adxl345_tap_int_reg[] = { 48 49 [ADXL345_SINGLE_TAP] = ADXL345_INT_SINGLE_TAP, 50 + [ADXL345_DOUBLE_TAP] = ADXL345_INT_DOUBLE_TAP, 49 51 }; 50 52 51 53 enum adxl345_tap_time_type { 54 + ADXL345_TAP_TIME_LATENT, 55 + ADXL345_TAP_TIME_WINDOW, 52 56 ADXL345_TAP_TIME_DUR, 53 57 }; 54 58 55 59 static const unsigned int adxl345_tap_time_reg[] = { 60 + [ADXL345_TAP_TIME_LATENT] = ADXL345_REG_LATENT, 61 + [ADXL345_TAP_TIME_WINDOW] = ADXL345_REG_WINDOW, 56 62 [ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR, 57 63 }; 58 64 ··· 71 65 u8 fifo_mode; 72 66 73 67 u32 tap_duration_us; 68 + u32 tap_latent_us; 69 + u32 tap_window_us; 74 70 75 71 __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); 76 72 }; ··· 85 77 .mask_separate = BIT(IIO_EV_INFO_ENABLE), 86 78 .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | 87 79 BIT(IIO_EV_INFO_TIMEOUT), 80 + }, 81 + { 82 + /* double tap */ 83 + .type = IIO_EV_TYPE_GESTURE, 84 + .dir = IIO_EV_DIR_DOUBLETAP, 85 + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | 86 + BIT(IIO_EV_INFO_RESET_TIMEOUT) | 87 + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), 88 88 }, 89 89 }; 90 90 ··· 179 163 unsigned int tap_threshold; 180 164 bool axis_valid; 181 165 bool singletap_args_valid = false; 166 + bool doubletap_args_valid = false; 182 167 bool en = false; 183 168 u32 axis_ctrl; 184 169 int ret; ··· 200 183 */ 201 184 singletap_args_valid = tap_threshold > 0 && st->tap_duration_us > 0; 202 185 203 - if (type == ADXL345_SINGLE_TAP) 186 + if (type == ADXL345_SINGLE_TAP) { 204 187 en = axis_valid && singletap_args_valid; 188 + } else { 189 + /* doubletap: Window must be equal or greater than latent! */ 190 + doubletap_args_valid = st->tap_latent_us > 0 && 191 + st->tap_window_us > 0 && 192 + st->tap_window_us >= st->tap_latent_us; 193 + 194 + en = axis_valid && singletap_args_valid && doubletap_args_valid; 195 + } 205 196 206 197 if (state && en) 207 198 int_map |= adxl345_tap_int_reg[type]; ··· 293 268 return _adxl345_set_tap_int(st, ADXL345_SINGLE_TAP, en); 294 269 } 295 270 271 + static int adxl345_set_doubletap_en(struct adxl345_state *st, bool en) 272 + { 273 + return _adxl345_set_tap_int(st, ADXL345_DOUBLE_TAP, en); 274 + } 275 + 296 276 static int _adxl345_set_tap_time(struct adxl345_state *st, 297 277 enum adxl345_tap_time_type type, u32 val_us) 298 278 { 299 279 unsigned int regval; 300 280 301 281 switch (type) { 282 + case ADXL345_TAP_TIME_WINDOW: 283 + st->tap_window_us = val_us; 284 + break; 285 + case ADXL345_TAP_TIME_LATENT: 286 + st->tap_latent_us = val_us; 287 + break; 302 288 case ADXL345_TAP_TIME_DUR: 303 289 st->tap_duration_us = val_us; 304 290 break; ··· 339 303 return -EINVAL; 340 304 341 305 return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_DUR, val_fract_us); 306 + } 307 + 308 + static int adxl345_set_tap_window(struct adxl345_state *st, u32 val_int, 309 + u32 val_fract_us) 310 + { 311 + /* 312 + * Max value is 255 * 1250 us = 0.318750 seconds 313 + * 314 + * Note: the scaling is similar to the scaling in the ADXL380 315 + */ 316 + if (val_int || val_fract_us > 318750) 317 + return -EINVAL; 318 + 319 + return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_WINDOW, val_fract_us); 320 + } 321 + 322 + static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int, 323 + u32 val_fract_us) 324 + { 325 + /* 326 + * Max value is 255 * 1250 us = 0.318750 seconds 327 + * 328 + * Note: the scaling is similar to the scaling in the ADXL380 329 + */ 330 + if (val_int || val_fract_us > 318750) 331 + return -EINVAL; 332 + 333 + return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us); 342 334 } 343 335 344 336 static int adxl345_read_raw(struct iio_dev *indio_dev, ··· 472 408 if (ret) 473 409 return ret; 474 410 return int_en; 411 + case IIO_EV_DIR_DOUBLETAP: 412 + ret = adxl345_is_tap_en(st, chan->channel2, 413 + ADXL345_DOUBLE_TAP, &int_en); 414 + if (ret) 415 + return ret; 416 + return int_en; 475 417 default: 476 418 return -EINVAL; 477 419 } ··· 499 429 switch (dir) { 500 430 case IIO_EV_DIR_SINGLETAP: 501 431 return adxl345_set_singletap_en(st, chan->channel2, state); 432 + case IIO_EV_DIR_DOUBLETAP: 433 + return adxl345_set_doubletap_en(st, state); 502 434 default: 503 435 return -EINVAL; 504 436 } ··· 540 468 *val = st->tap_duration_us; 541 469 *val2 = 1000000; 542 470 return IIO_VAL_FRACTIONAL; 471 + case IIO_EV_INFO_RESET_TIMEOUT: 472 + *val = st->tap_window_us; 473 + *val2 = 1000000; 474 + return IIO_VAL_FRACTIONAL; 475 + case IIO_EV_INFO_TAP2_MIN_DELAY: 476 + *val = st->tap_latent_us; 477 + *val2 = 1000000; 478 + return IIO_VAL_FRACTIONAL; 543 479 default: 544 480 return -EINVAL; 545 481 } ··· 581 501 break; 582 502 case IIO_EV_INFO_TIMEOUT: 583 503 ret = adxl345_set_tap_duration(st, val, val2); 504 + if (ret) 505 + return ret; 506 + break; 507 + case IIO_EV_INFO_RESET_TIMEOUT: 508 + ret = adxl345_set_tap_window(st, val, val2); 509 + if (ret) 510 + return ret; 511 + break; 512 + case IIO_EV_INFO_TAP2_MIN_DELAY: 513 + ret = adxl345_set_tap_latent(st, val, val2); 584 514 if (ret) 585 515 return ret; 586 516 break; ··· 841 751 return ret; 842 752 } 843 753 754 + if (FIELD_GET(ADXL345_INT_DOUBLE_TAP, int_stat)) { 755 + ret = iio_push_event(indio_dev, 756 + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, tap_dir, 757 + IIO_EV_TYPE_GESTURE, 758 + IIO_EV_DIR_DOUBLETAP), 759 + ts); 760 + if (ret) 761 + return ret; 762 + } 763 + 844 764 if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) { 845 765 samples = adxl345_get_samples(st); 846 766 if (samples < 0) ··· 975 875 /* Init with reasonable values */ 976 876 tap_threshold = 48; /* 48 [0x30] -> ~3g */ 977 877 st->tap_duration_us = 16; /* 16 [0x10] -> .010 */ 878 + st->tap_window_us = 64; /* 64 [0x40] -> .080 */ 879 + st->tap_latent_us = 16; /* 16 [0x10] -> .020 */ 978 880 979 881 indio_dev->name = st->info->name; 980 882 indio_dev->info = &adxl345_info;