OSDN Git Service

iio: adc: mcp3911: add support to set PGA
authorMarcus Folkesson <marcus.folkesson@gmail.com>
Thu, 22 Sep 2022 19:46:39 +0000 (21:46 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 23 Nov 2022 19:43:57 +0000 (19:43 +0000)
Add support for setting the Programmable Gain Amplifiers by adjust the
scale value.

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220922194639.1118971-1-marcus.folkesson@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/mcp3911.c

index 76b334f..974c5bd 100644 (file)
@@ -29,6 +29,8 @@
 #define MCP3911_REG_MOD                        0x06
 #define MCP3911_REG_PHASE              0x07
 #define MCP3911_REG_GAIN               0x09
+#define MCP3911_GAIN_MASK(ch)          (GENMASK(2, 0) << 3 * ch)
+#define MCP3911_GAIN_VAL(ch, val)      ((val << 3 * ch) & MCP3911_GAIN_MASK(ch))
 
 #define MCP3911_REG_STATUSCOM          0x0a
 #define MCP3911_STATUSCOM_DRHIZ         BIT(12)
 #define MCP3911_REG_MASK               GENMASK(4, 1)
 
 #define MCP3911_NUM_CHANNELS           2
+#define MCP3911_NUM_SCALES             6
 
 static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 };
+static u32 mcp3911_scale_table[MCP3911_NUM_SCALES][2];
 
 struct mcp3911 {
        struct spi_device *spi;
@@ -70,6 +74,7 @@ struct mcp3911 {
        struct clk *clki;
        u32 dev_addr;
        struct iio_trigger *trig;
+       u32 gain[MCP3911_NUM_CHANNELS];
        struct {
                u32 channels[MCP3911_NUM_CHANNELS];
                s64 ts __aligned(8);
@@ -146,6 +151,11 @@ static int mcp3911_read_avail(struct iio_dev *indio_dev,
                *vals = mcp3911_osr_table;
                *length = ARRAY_SIZE(mcp3911_osr_table);
                return IIO_AVAIL_LIST;
+       case IIO_CHAN_INFO_SCALE:
+               *type = IIO_VAL_INT_PLUS_NANO;
+               *vals = (int *)mcp3911_scale_table;
+               *length = ARRAY_SIZE(mcp3911_scale_table) * 2;
+               return IIO_AVAIL_LIST;
        default:
                return -EINVAL;
        }
@@ -190,29 +200,9 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev,
                break;
 
        case IIO_CHAN_INFO_SCALE:
-               if (adc->vref) {
-                       ret = regulator_get_voltage(adc->vref);
-                       if (ret < 0) {
-                               dev_err(indio_dev->dev.parent,
-                                       "failed to get vref voltage: %d\n",
-                                      ret);
-                               goto out;
-                       }
-
-                       *val = ret / 1000;
-               } else {
-                       *val = MCP3911_INT_VREF_MV;
-               }
-
-               /*
-                * For 24bit Conversion
-                * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
-                * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5)
-                */
-
-               /* val2 = (2^23 * 1.5) */
-               *val2 = 12582912;
-               ret = IIO_VAL_FRACTIONAL;
+               *val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0];
+               *val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1];
+               ret = IIO_VAL_INT_PLUS_NANO;
                break;
        }
 
@@ -230,6 +220,18 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
 
        mutex_lock(&adc->lock);
        switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
+                       if (val == mcp3911_scale_table[i][0] &&
+                               val2 == mcp3911_scale_table[i][1]) {
+
+                               adc->gain[channel->channel] = BIT(i);
+                               ret = mcp3911_update(adc, MCP3911_REG_GAIN,
+                                               MCP3911_GAIN_MASK(channel->channel),
+                                               MCP3911_GAIN_VAL(channel->channel, i), 1);
+                       }
+               }
+               break;
        case IIO_CHAN_INFO_OFFSET:
                if (val2 != 0) {
                        ret = -EINVAL;
@@ -265,6 +267,44 @@ out:
        return ret;
 }
 
+static int mcp3911_calc_scale_table(struct mcp3911 *adc)
+{
+       u32 ref = MCP3911_INT_VREF_MV;
+       u32 div;
+       int ret;
+       u64 tmp;
+
+       if (adc->vref) {
+               ret = regulator_get_voltage(adc->vref);
+               if (ret < 0) {
+                       dev_err(&adc->spi->dev,
+                               "failed to get vref voltage: %d\n",
+                              ret);
+                       return ret;
+               }
+
+               ref = ret / 1000;
+       }
+
+       /*
+        * For 24-bit Conversion
+        * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
+        * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5)
+        *
+        * ref = Reference voltage
+        * div = (2^23 * 1.5 * gain) = 12582912 * gain
+        */
+       for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
+               div = 12582912 * BIT(i);
+               tmp = div_s64((s64)ref * 1000000000LL, div);
+
+               mcp3911_scale_table[i][0] = 0;
+               mcp3911_scale_table[i][1] = tmp;
+       }
+
+       return 0;
+}
+
 #define MCP3911_CHAN(idx) {                                    \
                .type = IIO_VOLTAGE,                            \
                .indexed = 1,                                   \
@@ -274,8 +314,10 @@ out:
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
                        BIT(IIO_CHAN_INFO_OFFSET) |             \
                        BIT(IIO_CHAN_INFO_SCALE),               \
-               .info_mask_shared_by_type_available =           \
+               .info_mask_shared_by_type_available =           \
                        BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),  \
+               .info_mask_separate_available =                 \
+                       BIT(IIO_CHAN_INFO_SCALE),               \
                .scan_type = {                                  \
                        .sign = 's',                            \
                        .realbits = 24,                         \
@@ -482,6 +524,20 @@ static int mcp3911_probe(struct spi_device *spi)
        if (ret)
                return ret;
 
+       ret = mcp3911_calc_scale_table(adc);
+       if (ret)
+               return ret;
+
+       /* Set gain to 1 for all channels */
+       for (int i = 0; i < MCP3911_NUM_CHANNELS; i++) {
+               adc->gain[i] = 1;
+               ret = mcp3911_update(adc, MCP3911_REG_GAIN,
+                               MCP3911_GAIN_MASK(i),
+                               MCP3911_GAIN_VAL(i, 0), 1);
+               if (ret)
+                       return ret;
+       }
+
        indio_dev->name = spi_get_device_id(spi)->name;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->info = &mcp3911_info;