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: adc: ad4695: implement calibration support

The AD4695 has a calibration feature that allows the user to compensate
for variations in the analog front end. This implements this feature in
the driver using the standard `calibgain` and `calibbias` attributes.

Signed-off-by: David Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20240820-ad4695-gain-offset-v1-2-c8f6e3b47551@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

David Lechner and committed by
Jonathan Cameron
7763e40f 2ba49fc4

+156 -2
+156 -2
drivers/iio/adc/ad4695.c
··· 23 23 #include <linux/iio/iio.h> 24 24 #include <linux/iio/triggered_buffer.h> 25 25 #include <linux/iio/trigger_consumer.h> 26 + #include <linux/minmax.h> 26 27 #include <linux/property.h> 27 28 #include <linux/regmap.h> 28 29 #include <linux/regulator/consumer.h> ··· 226 225 .indexed = 1, 227 226 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 228 227 BIT(IIO_CHAN_INFO_SCALE) | 229 - BIT(IIO_CHAN_INFO_OFFSET), 228 + BIT(IIO_CHAN_INFO_OFFSET) | 229 + BIT(IIO_CHAN_INFO_CALIBSCALE) | 230 + BIT(IIO_CHAN_INFO_CALIBBIAS), 231 + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | 232 + BIT(IIO_CHAN_INFO_CALIBBIAS), 230 233 .scan_type = { 231 234 .sign = 'u', 232 235 .realbits = 16, ··· 624 619 struct ad4695_state *st = iio_priv(indio_dev); 625 620 struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index]; 626 621 u8 realbits = chan->scan_type.realbits; 627 - int ret; 622 + unsigned int reg_val; 623 + int ret, tmp; 628 624 629 625 switch (mask) { 630 626 case IIO_CHAN_INFO_RAW: ··· 676 670 default: 677 671 return -EINVAL; 678 672 } 673 + case IIO_CHAN_INFO_CALIBSCALE: 674 + switch (chan->type) { 675 + case IIO_VOLTAGE: 676 + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { 677 + ret = regmap_read(st->regmap16, 678 + AD4695_REG_GAIN_IN(chan->scan_index), 679 + &reg_val); 680 + if (ret) 681 + return ret; 682 + 683 + *val = reg_val; 684 + *val2 = 15; 685 + 686 + return IIO_VAL_FRACTIONAL_LOG2; 687 + } 688 + unreachable(); 689 + default: 690 + return -EINVAL; 691 + } 692 + case IIO_CHAN_INFO_CALIBBIAS: 693 + switch (chan->type) { 694 + case IIO_VOLTAGE: 695 + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { 696 + ret = regmap_read(st->regmap16, 697 + AD4695_REG_OFFSET_IN(chan->scan_index), 698 + &reg_val); 699 + if (ret) 700 + return ret; 701 + 702 + tmp = sign_extend32(reg_val, 15); 703 + 704 + *val = tmp / 4; 705 + *val2 = abs(tmp) % 4 * MICRO / 4; 706 + 707 + if (tmp < 0 && *val2) { 708 + *val *= -1; 709 + *val2 *= -1; 710 + } 711 + 712 + return IIO_VAL_INT_PLUS_MICRO; 713 + } 714 + unreachable(); 715 + default: 716 + return -EINVAL; 717 + } 718 + default: 719 + return -EINVAL; 720 + } 721 + } 722 + 723 + static int ad4695_write_raw(struct iio_dev *indio_dev, 724 + struct iio_chan_spec const *chan, 725 + int val, int val2, long mask) 726 + { 727 + struct ad4695_state *st = iio_priv(indio_dev); 728 + unsigned int reg_val; 729 + 730 + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { 731 + switch (mask) { 732 + case IIO_CHAN_INFO_CALIBSCALE: 733 + switch (chan->type) { 734 + case IIO_VOLTAGE: 735 + if (val < 0 || val2 < 0) 736 + reg_val = 0; 737 + else if (val > 1) 738 + reg_val = U16_MAX; 739 + else 740 + reg_val = (val * (1 << 16) + 741 + mul_u64_u32_div(val2, 1 << 16, 742 + MICRO)) / 2; 743 + 744 + return regmap_write(st->regmap16, 745 + AD4695_REG_GAIN_IN(chan->scan_index), 746 + reg_val); 747 + default: 748 + return -EINVAL; 749 + } 750 + case IIO_CHAN_INFO_CALIBBIAS: 751 + switch (chan->type) { 752 + case IIO_VOLTAGE: 753 + if (val2 >= 0 && val > S16_MAX / 4) 754 + reg_val = S16_MAX; 755 + else if ((val2 < 0 ? -val : val) < S16_MIN / 4) 756 + reg_val = S16_MIN; 757 + else if (val2 < 0) 758 + reg_val = clamp_t(int, 759 + -(val * 4 + -val2 * 4 / MICRO), 760 + S16_MIN, S16_MAX); 761 + else if (val < 0) 762 + reg_val = clamp_t(int, 763 + val * 4 - val2 * 4 / MICRO, 764 + S16_MIN, S16_MAX); 765 + else 766 + reg_val = clamp_t(int, 767 + val * 4 + val2 * 4 / MICRO, 768 + S16_MIN, S16_MAX); 769 + 770 + return regmap_write(st->regmap16, 771 + AD4695_REG_OFFSET_IN(chan->scan_index), 772 + reg_val); 773 + default: 774 + return -EINVAL; 775 + } 776 + default: 777 + return -EINVAL; 778 + } 779 + } 780 + unreachable(); 781 + } 782 + 783 + static int ad4695_read_avail(struct iio_dev *indio_dev, 784 + struct iio_chan_spec const *chan, 785 + const int **vals, int *type, int *length, 786 + long mask) 787 + { 788 + static const int ad4695_calibscale_available[6] = { 789 + /* Range of 0 (inclusive) to 2 (exclusive) */ 790 + 0, 15, 1, 15, U16_MAX, 15 791 + }; 792 + static const int ad4695_calibbias_available[6] = { 793 + /* 794 + * Datasheet says FSR/8 which translates to signed/4. The step 795 + * depends on oversampling ratio which is always 1 for now. 796 + */ 797 + S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4 798 + }; 799 + 800 + switch (mask) { 801 + case IIO_CHAN_INFO_CALIBSCALE: 802 + switch (chan->type) { 803 + case IIO_VOLTAGE: 804 + *vals = ad4695_calibscale_available; 805 + *type = IIO_VAL_FRACTIONAL_LOG2; 806 + return IIO_AVAIL_RANGE; 807 + default: 808 + return -EINVAL; 809 + } 810 + case IIO_CHAN_INFO_CALIBBIAS: 811 + switch (chan->type) { 812 + case IIO_VOLTAGE: 813 + *vals = ad4695_calibbias_available; 814 + *type = IIO_VAL_INT_PLUS_MICRO; 815 + return IIO_AVAIL_RANGE; 816 + default: 817 + return -EINVAL; 818 + } 679 819 default: 680 820 return -EINVAL; 681 821 } ··· 857 705 858 706 static const struct iio_info ad4695_info = { 859 707 .read_raw = &ad4695_read_raw, 708 + .write_raw = &ad4695_write_raw, 709 + .read_avail = &ad4695_read_avail, 860 710 .debugfs_reg_access = &ad4695_debugfs_reg_access, 861 711 }; 862 712