]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: adc: ad4030: add support for ad4630-24 and ad4630-16
authorEsteban Blanc <eblanc@baylibre.com>
Fri, 14 Feb 2025 12:22:34 +0000 (13:22 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 16 Feb 2025 15:39:43 +0000 (15:39 +0000)
AD4630-24 and AD4630-16 are 2 channels ADCs. Both channels are
interleaved bit per bit on SDO line.

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

index d026535e5d9b03c663a2083451a1997318a15986..0fdf01d6d7f30dbe375a52eb82b41124721d3bee 100644 (file)
@@ -33,6 +33,8 @@
 #define AD4030_REG_PRODUCT_ID_H                                0x05
 #define AD4030_REG_CHIP_GRADE                          0x06
 #define     AD4030_REG_CHIP_GRADE_AD4030_24_GRADE      0x10
+#define     AD4030_REG_CHIP_GRADE_AD4630_16_GRADE      0x03
+#define     AD4030_REG_CHIP_GRADE_AD4630_24_GRADE      0x00
 #define     AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE      GENMASK(7, 3)
 #define AD4030_REG_SCRATCH_PAD                 0x0A
 #define AD4030_REG_SPI_REVISION                        0x0B
@@ -83,6 +85,7 @@
 #define AD4030_MAX_HARDWARE_CHANNEL_NB         2
 #define AD4030_MAX_IIO_CHANNEL_NB              5
 #define AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK        0b10
+#define AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK  0b1100
 #define AD4030_GAIN_MIDLE_POINT                        0x8000
 /*
  * This accounts for 1 sample per channel plus one s64 for the timestamp,
@@ -112,6 +115,13 @@ enum ad4030_out_mode {
        AD4030_OUT_DATA_MD_32_PATTERN,
 };
 
+enum {
+       AD4030_LANE_MD_1_PER_CH,
+       AD4030_LANE_MD_2_PER_CH,
+       AD4030_LANE_MD_4_PER_CH,
+       AD4030_LANE_MD_INTERLEAVED,
+};
+
 enum {
        AD4030_SCAN_TYPE_NORMAL,
        AD4030_SCAN_TYPE_AVG,
@@ -150,7 +160,11 @@ struct ad4030_state {
                struct {
                        s32 diff;
                        u8 common;
-               };
+               } single;
+               struct {
+                       s32 diff[2];
+                       u8 common[2];
+               } dual;
        } rx_data;
 };
 
@@ -514,19 +528,33 @@ static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
 static bool ad4030_is_common_byte_asked(struct ad4030_state *st,
                                        unsigned int mask)
 {
-       return mask & AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK;
+       return mask & (st->chip->num_voltage_inputs == 1 ?
+               AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK :
+               AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK);
 }
 
 static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
 {
        struct ad4030_state *st = iio_priv(indio_dev);
 
-       if (st->avg_log2 > 0)
+       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
+       } else if (ad4030_is_common_byte_asked(st, mask)) {
+               switch (st->chip->precision_bits) {
+               case 16:
+                       st->mode = AD4030_OUT_DATA_MD_16_DIFF_8_COM;
+                       break;
+
+               case 24:
+                       st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       } else {
                st->mode = AD4030_OUT_DATA_MD_DIFF;
+       }
 
        st->current_scan_type = iio_get_current_scan_type(indio_dev,
                                                          st->chip->channels);
@@ -538,11 +566,52 @@ static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
                                  st->mode);
 }
 
+/*
+ * Descramble 2 32bits numbers out of a 64bits. The bits are interleaved:
+ * 1 bit for first number, 1 bit for the second, and so on...
+ */
+static void ad4030_extract_interleaved(u8 *src, u32 *ch0, u32 *ch1)
+{
+       u8 h0, h1, l0, l1;
+       u32 out0, out1;
+       u8 *out0_raw = (u8 *)&out0;
+       u8 *out1_raw = (u8 *)&out1;
+
+       for (int i = 0; i < 4; i++) {
+               h0 = src[i * 2];
+               l1 = src[i * 2 + 1];
+               h1 = h0 << 1;
+               l0 = l1 >> 1;
+
+               h0 &= 0xAA;
+               l0 &= 0x55;
+               h1 &= 0xAA;
+               l1 &= 0x55;
+
+               h0 = (h0 | h0 << 001) & 0xCC;
+               h1 = (h1 | h1 << 001) & 0xCC;
+               l0 = (l0 | l0 >> 001) & 0x33;
+               l1 = (l1 | l1 >> 001) & 0x33;
+               h0 = (h0 | h0 << 002) & 0xF0;
+               h1 = (h1 | h1 << 002) & 0xF0;
+               l0 = (l0 | l0 >> 002) & 0x0F;
+               l1 = (l1 | l1 >> 002) & 0x0F;
+
+               out0_raw[i] = h0 | l0;
+               out1_raw[i] = h1 | l1;
+       }
+
+       *ch0 = out0;
+       *ch1 = out1;
+}
+
 static int ad4030_conversion(struct iio_dev *indio_dev)
 {
        struct ad4030_state *st = iio_priv(indio_dev);
        unsigned char diff_realbytes =
                BITS_TO_BYTES(st->current_scan_type->realbits);
+       unsigned char diff_storagebytes =
+               BITS_TO_BYTES(st->current_scan_type->storagebits);
        unsigned int bytes_to_read;
        unsigned long cnv_nb = BIT(st->avg_log2);
        unsigned int i;
@@ -567,10 +636,23 @@ static int ad4030_conversion(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       if (st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM)
+       if (st->chip->num_voltage_inputs == 2)
+               ad4030_extract_interleaved(st->rx_data.raw,
+                                          &st->rx_data.dual.diff[0],
+                                          &st->rx_data.dual.diff[1]);
+
+       if (st->mode != AD4030_OUT_DATA_MD_16_DIFF_8_COM &&
+           st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM)
+               return 0;
+
+       if (st->chip->num_voltage_inputs == 1) {
+               st->rx_data.single.common = st->rx_data.raw[diff_realbytes];
                return 0;
+       }
 
-       st->rx_data.common = st->rx_data.raw[diff_realbytes];
+       for (i = 0; i < st->chip->num_voltage_inputs; i++)
+               st->rx_data.dual.common[i] =
+                       st->rx_data.raw[diff_storagebytes * i + diff_realbytes];
 
        return 0;
 }
@@ -585,14 +667,25 @@ static int ad4030_single_conversion(struct iio_dev *indio_dev,
        if (ret)
                return ret;
 
+       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);
+
        ret = ad4030_conversion(indio_dev);
        if (ret)
                return ret;
 
        if (chan->differential)
-               *val = st->rx_data.diff;
+               if (st->chip->num_voltage_inputs == 1)
+                       *val = st->rx_data.single.diff;
+               else
+                       *val = st->rx_data.dual.diff[chan->address];
        else
-               *val = st->rx_data.common;
+               if (st->chip->num_voltage_inputs == 1)
+                       *val = st->rx_data.single.common;
+               else
+                       *val = st->rx_data.dual.common[chan->address];
 
        return IIO_VAL_INT;
 }
@@ -874,10 +967,24 @@ static int ad4030_detect_chip_info(const struct ad4030_state *st)
 
 static int ad4030_config(struct ad4030_state *st)
 {
+       int ret;
+       u8 reg_modes;
+
        st->offset_avail[0] = (int)BIT(st->chip->precision_bits - 1) * -1;
        st->offset_avail[1] = 1;
        st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1;
 
+       if (st->chip->num_voltage_inputs > 1)
+               reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE,
+                                      AD4030_LANE_MD_INTERLEAVED);
+       else
+               reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE,
+                                      AD4030_LANE_MD_1_PER_CH);
+
+       ret = regmap_write(st->regmap, AD4030_REG_MODES, reg_modes);
+       if (ret)
+               return ret;
+
        if (st->vio_uv < AD4030_VIO_THRESHOLD_UV)
                return regmap_write(st->regmap, AD4030_REG_IO,
                                    AD4030_REG_IO_MASK_IO2X);
@@ -966,6 +1073,14 @@ static const unsigned long ad4030_channel_masks[] = {
        0,
 };
 
+static const unsigned long ad4630_channel_masks[] = {
+       /* Differential only */
+       BIT(1) | BIT(0),
+       /* Differential with common byte */
+       GENMASK(3, 0),
+       0,
+};
+
 static const struct iio_scan_type ad4030_24_scan_types[] = {
        [AD4030_SCAN_TYPE_NORMAL] = {
                .sign = 's',
@@ -983,6 +1098,23 @@ static const struct iio_scan_type ad4030_24_scan_types[] = {
        },
 };
 
+static const struct iio_scan_type ad4030_16_scan_types[] = {
+       [AD4030_SCAN_TYPE_NORMAL] = {
+               .sign = 's',
+               .storagebits = 32,
+               .realbits = 16,
+               .shift = 16,
+               .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,
@@ -997,14 +1129,50 @@ static const struct ad4030_chip_info ad4030_24_chip_info = {
        .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
 };
 
+static const struct ad4030_chip_info ad4630_16_chip_info = {
+       .name = "ad4630-16",
+       .available_masks = ad4630_channel_masks,
+       .channels = {
+               AD4030_CHAN_DIFF(0, ad4030_16_scan_types),
+               AD4030_CHAN_DIFF(1, ad4030_16_scan_types),
+               AD4030_CHAN_CMO(2, 0),
+               AD4030_CHAN_CMO(3, 1),
+               IIO_CHAN_SOFT_TIMESTAMP(4),
+       },
+       .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE,
+       .precision_bits = 16,
+       .num_voltage_inputs = 2,
+       .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
+};
+
+static const struct ad4030_chip_info ad4630_24_chip_info = {
+       .name = "ad4630-24",
+       .available_masks = ad4630_channel_masks,
+       .channels = {
+               AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
+               AD4030_CHAN_DIFF(1, ad4030_24_scan_types),
+               AD4030_CHAN_CMO(2, 0),
+               AD4030_CHAN_CMO(3, 1),
+               IIO_CHAN_SOFT_TIMESTAMP(4),
+       },
+       .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE,
+       .precision_bits = 24,
+       .num_voltage_inputs = 2,
+       .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
+};
+
 static const struct spi_device_id ad4030_id_table[] = {
        { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info },
+       { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info },
+       { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(spi, ad4030_id_table);
 
 static const struct of_device_id ad4030_of_match[] = {
        { .compatible = "adi,ad4030-24", .data = &ad4030_24_chip_info },
+       { .compatible = "adi,ad4630-16", .data = &ad4630_16_chip_info },
+       { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(of, ad4030_of_match);