]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: adc: ad4030: add averaging support
authorEsteban Blanc <eblanc@baylibre.com>
Fri, 14 Feb 2025 12:22:33 +0000 (13:22 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 16 Feb 2025 15:39:15 +0000 (15:39 +0000)
This add support for the averaging mode of AD4030 using oversampling IIO
attribute

Signed-off-by: Esteban Blanc <eblanc@baylibre.com>
Link: https://patch.msgid.link/20250214-eblanc-ad4630_v1-v4-3-135dd66cab6a@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/ad4030.c

index 8188d44bdd664aa74a7adcaa1f836e79a54a6050..d026535e5d9b03c663a2083451a1997318a15986 100644 (file)
@@ -112,6 +112,11 @@ enum ad4030_out_mode {
        AD4030_OUT_DATA_MD_32_PATTERN,
 };
 
+enum {
+       AD4030_SCAN_TYPE_NORMAL,
+       AD4030_SCAN_TYPE_AVG,
+};
+
 struct ad4030_chip_info {
        const char *name;
        const unsigned long *available_masks;
@@ -127,10 +132,12 @@ struct ad4030_state {
        struct spi_device *spi;
        struct regmap *regmap;
        const struct ad4030_chip_info *chip;
+       const struct iio_scan_type *current_scan_type;
        struct gpio_desc *cnv_gpio;
        int vref_uv;
        int vio_uv;
        int offset_avail[3];
+       unsigned int avg_log2;
        enum ad4030_out_mode mode;
 
        /*
@@ -184,7 +191,11 @@ struct ad4030_state {
  * - voltage0-voltage1
  * - voltage2-voltage3
  */
-#define AD4030_CHAN_DIFF(_idx, _storage, _real, _shift) {              \
+#define AD4030_CHAN_DIFF(_idx, _scan_type) {                           \
+       .info_mask_shared_by_all =                                      \
+               BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),                  \
+       .info_mask_shared_by_all_available =                            \
+               BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),                  \
        .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |                \
                BIT(IIO_CHAN_INFO_CALIBSCALE) |                         \
                BIT(IIO_CHAN_INFO_CALIBBIAS) |                          \
@@ -198,15 +209,17 @@ struct ad4030_state {
        .channel2 = (_idx) * 2 + 1,                                     \
        .scan_index = (_idx),                                           \
        .differential = true,                                           \
-       .scan_type = {                                                  \
-               .sign = 's',                                            \
-               .storagebits = _storage,                                \
-               .realbits = _real,                                      \
-               .shift = _shift,                                        \
-               .endianness = IIO_BE,                                   \
-       },                                                              \
+       .has_ext_scan_type = 1,                                         \
+       .ext_scan_type = _scan_type,                                    \
+       .num_ext_scan_type = ARRAY_SIZE(_scan_type),                    \
 }
 
+static const int ad4030_average_modes[] = {
+       1, 2, 4, 8, 16, 32, 64, 128,
+       256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
+       65536,
+};
+
 static int ad4030_enter_config_mode(struct ad4030_state *st)
 {
        st->tx_data[0] = AD4030_REG_ACCESS;
@@ -356,10 +369,13 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev,
                                 int *val2)
 {
        struct ad4030_state *st = iio_priv(indio_dev);
+       const struct iio_scan_type *scan_type;
 
        if (chan->differential) {
+               scan_type = iio_get_current_scan_type(indio_dev,
+                                                     st->chip->channels);
                *val = (st->vref_uv * 2) / MILLI;
-               *val2 = chan->scan_type.realbits;
+               *val2 = scan_type->realbits;
                return IIO_VAL_FRACTIONAL_LOG2;
        }
 
@@ -474,6 +490,27 @@ static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev,
                                 st->tx_data, AD4030_REG_OFFSET_BYTES_NB);
 }
 
+static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
+{
+       struct ad4030_state *st = iio_priv(dev);
+       unsigned int avg_log2 = ilog2(avg_val);
+       unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1;
+       int ret;
+
+       if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx])
+               return -EINVAL;
+
+       ret = regmap_write(st->regmap, AD4030_REG_AVG,
+                          AD4030_REG_AVG_MASK_AVG_SYNC |
+                          FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2));
+       if (ret)
+               return ret;
+
+       st->avg_log2 = avg_log2;
+
+       return 0;
+}
+
 static bool ad4030_is_common_byte_asked(struct ad4030_state *st,
                                        unsigned int mask)
 {
@@ -484,11 +521,18 @@ static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
 {
        struct ad4030_state *st = iio_priv(indio_dev);
 
-       if (ad4030_is_common_byte_asked(st, mask))
+       if (st->avg_log2 > 0)
+               st->mode = AD4030_OUT_DATA_MD_30_AVERAGED_DIFF;
+       else if (ad4030_is_common_byte_asked(st, mask))
                st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM;
        else
                st->mode = AD4030_OUT_DATA_MD_DIFF;
 
+       st->current_scan_type = iio_get_current_scan_type(indio_dev,
+                                                         st->chip->channels);
+       if (IS_ERR(st->current_scan_type))
+               return PTR_ERR(st->current_scan_type);
+
        return regmap_update_bits(st->regmap, AD4030_REG_MODES,
                                  AD4030_REG_MODES_MASK_OUT_DATA_MODE,
                                  st->mode);
@@ -497,9 +541,11 @@ static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
 static int ad4030_conversion(struct iio_dev *indio_dev)
 {
        struct ad4030_state *st = iio_priv(indio_dev);
-       const struct iio_scan_type scan_type = indio_dev->channels->scan_type;
-       unsigned char diff_realbytes = BITS_TO_BYTES(scan_type.realbits);
+       unsigned char diff_realbytes =
+               BITS_TO_BYTES(st->current_scan_type->realbits);
        unsigned int bytes_to_read;
+       unsigned long cnv_nb = BIT(st->avg_log2);
+       unsigned int i;
        int ret;
 
        /* Number of bytes for one differential channel */
@@ -510,10 +556,12 @@ static int ad4030_conversion(struct iio_dev *indio_dev)
        /* Mulitiply by the number of hardware channels */
        bytes_to_read *= st->chip->num_voltage_inputs;
 
-       gpiod_set_value_cansleep(st->cnv_gpio, 1);
-       ndelay(AD4030_TCNVH_NS);
-       gpiod_set_value_cansleep(st->cnv_gpio, 0);
-       ndelay(st->chip->tcyc_ns);
+       for (i = 0; i < cnv_nb; i++) {
+               gpiod_set_value_cansleep(st->cnv_gpio, 1);
+               ndelay(AD4030_TCNVH_NS);
+               gpiod_set_value_cansleep(st->cnv_gpio, 0);
+               ndelay(st->chip->tcyc_ns);
+       }
 
        ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read);
        if (ret)
@@ -593,6 +641,12 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
                *type = IIO_VAL_INT_PLUS_NANO;
                return IIO_AVAIL_RANGE;
 
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               *vals = ad4030_average_modes;
+               *type = IIO_VAL_INT;
+               *length = ARRAY_SIZE(ad4030_average_modes);
+               return IIO_AVAIL_LIST;
+
        default:
                return -EINVAL;
        }
@@ -602,6 +656,8 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev,
                                    struct iio_chan_spec const *chan, int *val,
                                    int *val2, long info)
 {
+       struct ad4030_state *st = iio_priv(indio_dev);
+
        switch (info) {
        case IIO_CHAN_INFO_RAW:
                return ad4030_single_conversion(indio_dev, chan, val);
@@ -612,6 +668,10 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_CALIBBIAS:
                return ad4030_get_chan_calibbias(indio_dev, chan, val);
 
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               *val = BIT(st->avg_log2);
+               return IIO_VAL_INT;
+
        default:
                return -EINVAL;
        }
@@ -650,6 +710,9 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev,
                        return -EINVAL;
                return ad4030_set_chan_calibbias(indio_dev, chan, val);
 
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               return ad4030_set_avg_frame_len(indio_dev, val);
+
        default:
                return -EINVAL;
        }
@@ -701,12 +764,21 @@ static int ad4030_read_label(struct iio_dev *indio_dev,
        return sprintf(label, "common-mode%lu\n", chan->address);
 }
 
+static int ad4030_get_current_scan_type(const struct iio_dev *indio_dev,
+                                       const struct iio_chan_spec *chan)
+{
+       struct ad4030_state *st = iio_priv(indio_dev);
+
+       return st->avg_log2 ? AD4030_SCAN_TYPE_AVG : AD4030_SCAN_TYPE_NORMAL;
+}
+
 static const struct iio_info ad4030_iio_info = {
        .read_avail = ad4030_read_avail,
        .read_raw = ad4030_read_raw,
        .write_raw = ad4030_write_raw,
        .debugfs_reg_access = ad4030_reg_access,
        .read_label = ad4030_read_label,
+       .get_current_scan_type = ad4030_get_current_scan_type,
 };
 
 static int ad4030_buffer_preenable(struct iio_dev *indio_dev)
@@ -714,8 +786,21 @@ static int ad4030_buffer_preenable(struct iio_dev *indio_dev)
        return ad4030_set_mode(indio_dev, *indio_dev->active_scan_mask);
 }
 
+static bool ad4030_validate_scan_mask(struct iio_dev *indio_dev,
+                                     const unsigned long *scan_mask)
+{
+       struct ad4030_state *st = iio_priv(indio_dev);
+
+       /* Asking for both common channels and averaging */
+       if (st->avg_log2 && ad4030_is_common_byte_asked(st, *scan_mask))
+               return false;
+
+       return true;
+}
+
 static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = {
        .preenable = ad4030_buffer_preenable,
+       .validate_scan_mask = ad4030_validate_scan_mask,
 };
 
 static int ad4030_regulators_get(struct ad4030_state *st)
@@ -881,11 +966,28 @@ static const unsigned long ad4030_channel_masks[] = {
        0,
 };
 
+static const struct iio_scan_type ad4030_24_scan_types[] = {
+       [AD4030_SCAN_TYPE_NORMAL] = {
+               .sign = 's',
+               .storagebits = 32,
+               .realbits = 24,
+               .shift = 8,
+               .endianness = IIO_BE,
+       },
+       [AD4030_SCAN_TYPE_AVG] = {
+               .sign = 's',
+               .storagebits = 32,
+               .realbits = 30,
+               .shift = 2,
+               .endianness = IIO_BE,
+       },
+};
+
 static const struct ad4030_chip_info ad4030_24_chip_info = {
        .name = "ad4030-24",
        .available_masks = ad4030_channel_masks,
        .channels = {
-               AD4030_CHAN_DIFF(0, 32, 24, 8),
+               AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
                AD4030_CHAN_CMO(1, 0),
                IIO_CHAN_SOFT_TIMESTAMP(2),
        },