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

iio: accel: fxls8962af: add threshold event handling

Add event channels that control the creation of motion events.

Signed-off-by: Sean Nyekjaer <sean@geanix.com>
Link: https://lore.kernel.org/r/20210920114221.1595543-1-sean@geanix.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Sean Nyekjaer and committed by
Jonathan Cameron
131fb9f2 d0a4c17b

+298 -3
+298 -3
drivers/iio/accel/fxls8962af-core.c
··· 22 22 #include <linux/regmap.h> 23 23 24 24 #include <linux/iio/buffer.h> 25 + #include <linux/iio/events.h> 25 26 #include <linux/iio/iio.h> 26 27 #include <linux/iio/kfifo_buf.h> 27 28 #include <linux/iio/sysfs.h> ··· 31 30 32 31 #define FXLS8962AF_INT_STATUS 0x00 33 32 #define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0) 33 + #define FXLS8962AF_INT_STATUS_SRC_SDCD_OT BIT(4) 34 34 #define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5) 35 35 #define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7) 36 36 #define FXLS8962AF_TEMP_OUT 0x01 ··· 75 73 #define FXLS8962AF_ASLP_COUNT_LSB 0x1e 76 74 77 75 #define FXLS8962AF_INT_EN 0x20 76 + #define FXLS8962AF_INT_EN_SDCD_OT_EN BIT(5) 78 77 #define FXLS8962AF_INT_EN_BUF_EN BIT(6) 79 78 #define FXLS8962AF_INT_PIN_SEL 0x21 80 79 #define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0) ··· 99 96 #define FXLS8962AF_ORIENT_THS_REG 0x2c 100 97 101 98 #define FXLS8962AF_SDCD_INT_SRC1 0x2d 99 + #define FXLS8962AF_SDCD_INT_SRC1_X_OT BIT(5) 100 + #define FXLS8962AF_SDCD_INT_SRC1_X_POL BIT(4) 101 + #define FXLS8962AF_SDCD_INT_SRC1_Y_OT BIT(3) 102 + #define FXLS8962AF_SDCD_INT_SRC1_Y_POL BIT(2) 103 + #define FXLS8962AF_SDCD_INT_SRC1_Z_OT BIT(1) 104 + #define FXLS8962AF_SDCD_INT_SRC1_Z_POL BIT(0) 102 105 #define FXLS8962AF_SDCD_INT_SRC2 0x2e 103 106 #define FXLS8962AF_SDCD_CONFIG1 0x2f 107 + #define FXLS8962AF_SDCD_CONFIG1_Z_OT_EN BIT(3) 108 + #define FXLS8962AF_SDCD_CONFIG1_Y_OT_EN BIT(4) 109 + #define FXLS8962AF_SDCD_CONFIG1_X_OT_EN BIT(5) 110 + #define FXLS8962AF_SDCD_CONFIG1_OT_ELE BIT(7) 104 111 #define FXLS8962AF_SDCD_CONFIG2 0x30 112 + #define FXLS8962AF_SDCD_CONFIG2_SDCD_EN BIT(7) 113 + #define FXLS8962AF_SC2_REF_UPDM_AC GENMASK(6, 5) 105 114 #define FXLS8962AF_SDCD_OT_DBCNT 0x31 106 115 #define FXLS8962AF_SDCD_WT_DBCNT 0x32 107 116 #define FXLS8962AF_SDCD_LTHS_LSB 0x33 ··· 167 152 int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ 168 153 struct iio_mount_matrix orientation; 169 154 u8 watermark; 155 + u8 enable_event; 156 + u16 lower_thres; 157 + u16 upper_thres; 170 158 }; 171 159 172 160 const struct regmap_config fxls8962af_regmap_conf = { ··· 256 238 } 257 239 258 240 ret = regmap_bulk_read(data->regmap, chan->address, 259 - &raw_val, (chan->scan_type.storagebits / 8)); 241 + &raw_val, sizeof(data->lower_thres)); 260 242 261 243 if (!is_active) 262 244 fxls8962af_power_off(data); ··· 469 451 } 470 452 } 471 453 454 + static int fxls8962af_event_setup(struct fxls8962af_data *data, int state) 455 + { 456 + /* Enable wakeup interrupt */ 457 + int mask = FXLS8962AF_INT_EN_SDCD_OT_EN; 458 + int value = state ? mask : 0; 459 + 460 + return regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, mask, value); 461 + } 462 + 472 463 static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) 473 464 { 474 465 struct fxls8962af_data *data = iio_priv(indio_dev); ··· 489 462 490 463 return 0; 491 464 } 465 + 466 + static int __fxls8962af_set_thresholds(struct fxls8962af_data *data, 467 + const struct iio_chan_spec *chan, 468 + enum iio_event_direction dir, 469 + int val) 470 + { 471 + switch (dir) { 472 + case IIO_EV_DIR_FALLING: 473 + data->lower_thres = val; 474 + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, 475 + &data->lower_thres, sizeof(data->lower_thres)); 476 + case IIO_EV_DIR_RISING: 477 + data->upper_thres = val; 478 + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, 479 + &data->upper_thres, sizeof(data->upper_thres)); 480 + default: 481 + return -EINVAL; 482 + } 483 + } 484 + 485 + static int fxls8962af_read_event(struct iio_dev *indio_dev, 486 + const struct iio_chan_spec *chan, 487 + enum iio_event_type type, 488 + enum iio_event_direction dir, 489 + enum iio_event_info info, 490 + int *val, int *val2) 491 + { 492 + struct fxls8962af_data *data = iio_priv(indio_dev); 493 + int ret; 494 + 495 + if (type != IIO_EV_TYPE_THRESH) 496 + return -EINVAL; 497 + 498 + switch (dir) { 499 + case IIO_EV_DIR_FALLING: 500 + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, 501 + &data->lower_thres, sizeof(data->lower_thres)); 502 + if (ret) 503 + return ret; 504 + 505 + *val = sign_extend32(data->lower_thres, chan->scan_type.realbits - 1); 506 + return IIO_VAL_INT; 507 + case IIO_EV_DIR_RISING: 508 + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, 509 + &data->upper_thres, sizeof(data->upper_thres)); 510 + if (ret) 511 + return ret; 512 + 513 + *val = sign_extend32(data->upper_thres, chan->scan_type.realbits - 1); 514 + return IIO_VAL_INT; 515 + default: 516 + return -EINVAL; 517 + } 518 + } 519 + 520 + static int fxls8962af_write_event(struct iio_dev *indio_dev, 521 + const struct iio_chan_spec *chan, 522 + enum iio_event_type type, 523 + enum iio_event_direction dir, 524 + enum iio_event_info info, 525 + int val, int val2) 526 + { 527 + struct fxls8962af_data *data = iio_priv(indio_dev); 528 + int ret, val_masked; 529 + 530 + if (type != IIO_EV_TYPE_THRESH) 531 + return -EINVAL; 532 + 533 + if (val < -2048 || val > 2047) 534 + return -EINVAL; 535 + 536 + if (data->enable_event) 537 + return -EBUSY; 538 + 539 + val_masked = val & GENMASK(11, 0); 540 + if (fxls8962af_is_active(data)) { 541 + ret = fxls8962af_standby(data); 542 + if (ret) 543 + return ret; 544 + 545 + ret = __fxls8962af_set_thresholds(data, chan, dir, val_masked); 546 + if (ret) 547 + return ret; 548 + 549 + return fxls8962af_active(data); 550 + } else { 551 + return __fxls8962af_set_thresholds(data, chan, dir, val_masked); 552 + } 553 + } 554 + 555 + static int 556 + fxls8962af_read_event_config(struct iio_dev *indio_dev, 557 + const struct iio_chan_spec *chan, 558 + enum iio_event_type type, 559 + enum iio_event_direction dir) 560 + { 561 + struct fxls8962af_data *data = iio_priv(indio_dev); 562 + 563 + if (type != IIO_EV_TYPE_THRESH) 564 + return -EINVAL; 565 + 566 + switch (chan->channel2) { 567 + case IIO_MOD_X: 568 + return !!(FXLS8962AF_SDCD_CONFIG1_X_OT_EN & data->enable_event); 569 + case IIO_MOD_Y: 570 + return !!(FXLS8962AF_SDCD_CONFIG1_Y_OT_EN & data->enable_event); 571 + case IIO_MOD_Z: 572 + return !!(FXLS8962AF_SDCD_CONFIG1_Z_OT_EN & data->enable_event); 573 + default: 574 + return -EINVAL; 575 + } 576 + } 577 + 578 + static int 579 + fxls8962af_write_event_config(struct iio_dev *indio_dev, 580 + const struct iio_chan_spec *chan, 581 + enum iio_event_type type, 582 + enum iio_event_direction dir, int state) 583 + { 584 + struct fxls8962af_data *data = iio_priv(indio_dev); 585 + u8 enable_event, enable_bits; 586 + int ret, value; 587 + 588 + if (type != IIO_EV_TYPE_THRESH) 589 + return -EINVAL; 590 + 591 + switch (chan->channel2) { 592 + case IIO_MOD_X: 593 + enable_bits = FXLS8962AF_SDCD_CONFIG1_X_OT_EN; 594 + break; 595 + case IIO_MOD_Y: 596 + enable_bits = FXLS8962AF_SDCD_CONFIG1_Y_OT_EN; 597 + break; 598 + case IIO_MOD_Z: 599 + enable_bits = FXLS8962AF_SDCD_CONFIG1_Z_OT_EN; 600 + break; 601 + default: 602 + return -EINVAL; 603 + } 604 + 605 + if (state) 606 + enable_event = data->enable_event | enable_bits; 607 + else 608 + enable_event = data->enable_event & ~enable_bits; 609 + 610 + if (data->enable_event == enable_event) 611 + return 0; 612 + 613 + ret = fxls8962af_standby(data); 614 + if (ret) 615 + return ret; 616 + 617 + /* Enable events */ 618 + value = enable_event | FXLS8962AF_SDCD_CONFIG1_OT_ELE; 619 + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG1, value); 620 + if (ret) 621 + return ret; 622 + 623 + /* 624 + * Enable update of SDCD_REF_X/Y/Z values with the current decimated and 625 + * trimmed X/Y/Z acceleration input data. This allows for acceleration 626 + * slope detection with Data(n) to Data(n–1) always used as the input 627 + * to the window comparator. 628 + */ 629 + value = enable_event ? 630 + FXLS8962AF_SDCD_CONFIG2_SDCD_EN | FXLS8962AF_SC2_REF_UPDM_AC : 631 + 0x00; 632 + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG2, value); 633 + if (ret) 634 + return ret; 635 + 636 + ret = fxls8962af_event_setup(data, state); 637 + if (ret) 638 + return ret; 639 + 640 + data->enable_event = enable_event; 641 + 642 + if (data->enable_event) { 643 + fxls8962af_active(data); 644 + ret = fxls8962af_power_on(data); 645 + } else { 646 + ret = iio_device_claim_direct_mode(indio_dev); 647 + if (ret) 648 + return ret; 649 + 650 + /* Not in buffered mode so disable power */ 651 + ret = fxls8962af_power_off(data); 652 + 653 + iio_device_release_direct_mode(indio_dev); 654 + } 655 + 656 + return ret; 657 + } 658 + 659 + static const struct iio_event_spec fxls8962af_event[] = { 660 + { 661 + .type = IIO_EV_TYPE_THRESH, 662 + .dir = IIO_EV_DIR_EITHER, 663 + .mask_separate = BIT(IIO_EV_INFO_ENABLE), 664 + }, 665 + { 666 + .type = IIO_EV_TYPE_THRESH, 667 + .dir = IIO_EV_DIR_FALLING, 668 + .mask_separate = BIT(IIO_EV_INFO_VALUE), 669 + }, 670 + { 671 + .type = IIO_EV_TYPE_THRESH, 672 + .dir = IIO_EV_DIR_RISING, 673 + .mask_separate = BIT(IIO_EV_INFO_VALUE), 674 + }, 675 + }; 492 676 493 677 #define FXLS8962AF_CHANNEL(axis, reg, idx) { \ 494 678 .type = IIO_ACCEL, \ ··· 719 481 .shift = 4, \ 720 482 .endianness = IIO_BE, \ 721 483 }, \ 484 + .event_spec = fxls8962af_event, \ 485 + .num_event_specs = ARRAY_SIZE(fxls8962af_event), \ 722 486 } 723 487 724 488 #define FXLS8962AF_TEMP_CHANNEL { \ ··· 762 522 .read_raw = &fxls8962af_read_raw, 763 523 .write_raw = &fxls8962af_write_raw, 764 524 .write_raw_get_fmt = fxls8962af_write_raw_get_fmt, 525 + .read_event_value = fxls8962af_read_event, 526 + .write_event_value = fxls8962af_write_event, 527 + .read_event_config = fxls8962af_read_event_config, 528 + .write_event_config = fxls8962af_write_event_config, 765 529 .read_avail = fxls8962af_read_avail, 766 530 .hwfifo_set_watermark = fxls8962af_set_watermark, 767 531 }; ··· 849 605 850 606 ret = __fxls8962af_fifo_set_mode(data, false); 851 607 852 - fxls8962af_active(data); 608 + if (data->enable_event) 609 + fxls8962af_active(data); 853 610 854 611 return ret; 855 612 } ··· 859 614 { 860 615 struct fxls8962af_data *data = iio_priv(indio_dev); 861 616 862 - return fxls8962af_power_off(data); 617 + if (!data->enable_event) 618 + fxls8962af_power_off(data); 619 + 620 + return 0; 863 621 } 864 622 865 623 static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = { ··· 973 725 return count; 974 726 } 975 727 728 + static int fxls8962af_event_interrupt(struct iio_dev *indio_dev) 729 + { 730 + struct fxls8962af_data *data = iio_priv(indio_dev); 731 + s64 ts = iio_get_time_ns(indio_dev); 732 + unsigned int reg; 733 + u64 ev_code; 734 + int ret; 735 + 736 + ret = regmap_read(data->regmap, FXLS8962AF_SDCD_INT_SRC1, &reg); 737 + if (ret) 738 + return ret; 739 + 740 + if (reg & FXLS8962AF_SDCD_INT_SRC1_X_OT) { 741 + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_X_POL ? 742 + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; 743 + iio_push_event(indio_dev, 744 + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, 745 + IIO_EV_TYPE_THRESH, ev_code), ts); 746 + } 747 + 748 + if (reg & FXLS8962AF_SDCD_INT_SRC1_Y_OT) { 749 + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Y_POL ? 750 + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; 751 + iio_push_event(indio_dev, 752 + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, 753 + IIO_EV_TYPE_THRESH, ev_code), ts); 754 + } 755 + 756 + if (reg & FXLS8962AF_SDCD_INT_SRC1_Z_OT) { 757 + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Z_POL ? 758 + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; 759 + iio_push_event(indio_dev, 760 + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, 761 + IIO_EV_TYPE_THRESH, ev_code), ts); 762 + } 763 + 764 + return 0; 765 + } 766 + 976 767 static irqreturn_t fxls8962af_interrupt(int irq, void *p) 977 768 { 978 769 struct iio_dev *indio_dev = p; ··· 1026 739 if (reg & FXLS8962AF_INT_STATUS_SRC_BUF) { 1027 740 ret = fxls8962af_fifo_flush(indio_dev); 1028 741 if (ret) 742 + return IRQ_NONE; 743 + 744 + return IRQ_HANDLED; 745 + } 746 + 747 + if (reg & FXLS8962AF_INT_STATUS_SRC_SDCD_OT) { 748 + ret = fxls8962af_event_interrupt(indio_dev); 749 + if (ret < 0) 1029 750 return IRQ_NONE; 1030 751 1031 752 return IRQ_HANDLED;