]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iio: adc: ad7124: Implement temperature measurement
authorUwe Kleine-König <u.kleine-koenig@baylibre.com>
Fri, 6 Dec 2024 17:28:42 +0000 (18:28 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 11 Dec 2024 19:20:48 +0000 (19:20 +0000)
If the maximal count of channels the driver supports isn't fully
utilized, add an attribute providing the internal temperature.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://patch.msgid.link/433211af8ac3f02dee58586ecb51d2e98246a095.1733504533.git.u.kleine-koenig@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/ad7124.c

index ab5c215fbcceb092ec50b864d3f839be87b17998..cceee334476de3a9cba7f1053ac5a15e525e73b4 100644 (file)
 #define AD7124_MAX_CONFIGS     8
 #define AD7124_MAX_CHANNELS    16
 
+/* AD7124 input sources */
+#define AD7124_INPUT_TEMPSENSOR        16
+#define AD7124_INPUT_AVSS      17
+
 enum ad7124_ids {
        ID_AD7124_4,
        ID_AD7124_8,
@@ -589,26 +593,59 @@ static int ad7124_read_raw(struct iio_dev *indio_dev,
 
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_SCALE:
-               mutex_lock(&st->cfgs_lock);
+               switch (chan->type) {
+               case IIO_VOLTAGE:
+                       mutex_lock(&st->cfgs_lock);
 
-               idx = st->channels[chan->address].cfg.pga_bits;
-               *val = st->channels[chan->address].cfg.vref_mv;
-               if (st->channels[chan->address].cfg.bipolar)
-                       *val2 = chan->scan_type.realbits - 1 + idx;
-               else
-                       *val2 = chan->scan_type.realbits + idx;
+                       idx = st->channels[chan->address].cfg.pga_bits;
+                       *val = st->channels[chan->address].cfg.vref_mv;
+                       if (st->channels[chan->address].cfg.bipolar)
+                               *val2 = chan->scan_type.realbits - 1 + idx;
+                       else
+                               *val2 = chan->scan_type.realbits + idx;
+
+                       mutex_unlock(&st->cfgs_lock);
+                       return IIO_VAL_FRACTIONAL_LOG2;
+
+               case IIO_TEMP:
+                       /*
+                        * According to the data sheet
+                        *   Temperature (°C)
+                        * = ((Conversion − 0x800000)/13584) − 272.5
+                        * = (Conversion − 0x800000 - 13584 * 272.5) / 13584
+                        * = (Conversion − 12090248) / 13584
+                        * So scale with 1000/13584 to yield °mC. Reduce by 8 to
+                        * 125/1698.
+                        */
+                       *val = 125;
+                       *val2 = 1698;
+                       return IIO_VAL_FRACTIONAL;
+
+               default:
+                       return -EINVAL;
+               }
 
-               mutex_unlock(&st->cfgs_lock);
-               return IIO_VAL_FRACTIONAL_LOG2;
        case IIO_CHAN_INFO_OFFSET:
-               mutex_lock(&st->cfgs_lock);
-               if (st->channels[chan->address].cfg.bipolar)
-                       *val = -(1 << (chan->scan_type.realbits - 1));
-               else
-                       *val = 0;
+               switch (chan->type) {
+               case IIO_VOLTAGE:
+                       mutex_lock(&st->cfgs_lock);
+                       if (st->channels[chan->address].cfg.bipolar)
+                               *val = -(1 << (chan->scan_type.realbits - 1));
+                       else
+                               *val = 0;
+
+                       mutex_unlock(&st->cfgs_lock);
+                       return IIO_VAL_INT;
+
+               case IIO_TEMP:
+                       /* see calculation above */
+                       *val = -12090248;
+                       return IIO_VAL_INT;
+
+               default:
+                       return -EINVAL;
+               }
 
-               mutex_unlock(&st->cfgs_lock);
-               return IIO_VAL_INT;
        case IIO_CHAN_INFO_SAMP_FREQ:
                mutex_lock(&st->cfgs_lock);
                *val = st->channels[chan->address].cfg.odr;
@@ -826,11 +863,10 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
        struct ad7124_channel *channels;
        struct iio_chan_spec *chan;
        unsigned int ain[2], channel = 0, tmp;
+       unsigned int num_channels;
        int ret;
 
-       st->num_channels = device_get_child_node_count(dev);
-       if (!st->num_channels)
-               return dev_err_probe(dev, -ENODEV, "no channel children\n");
+       num_channels = device_get_child_node_count(dev);
 
        /*
         * The driver assigns each logical channel defined in the device tree
@@ -839,9 +875,12 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
         * CHANNEL_15) as an additional channel register. The driver could be
         * improved to lift this limitation.
         */
-       if (st->num_channels > AD7124_MAX_CHANNELS)
+       if (num_channels > AD7124_MAX_CHANNELS)
                return dev_err_probe(dev, -EINVAL, "Too many channels defined\n");
 
+       /* Add one for temperature */
+       st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS);
+
        chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
                            sizeof(*chan), GFP_KERNEL);
        if (!chan)
@@ -862,7 +901,7 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
                        return dev_err_probe(dev, ret,
                                             "Failed to parse reg property of %pfwP\n", child);
 
-               if (channel >= indio_dev->num_channels)
+               if (channel >= num_channels)
                        return dev_err_probe(dev, -EINVAL,
                                             "Channel index >= number of channels in %pfwP\n", child);
 
@@ -902,6 +941,37 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
                chan[channel].channel2 = ain[1];
        }
 
+       if (num_channels < AD7124_MAX_CHANNELS) {
+               st->channels[num_channels] = (struct ad7124_channel) {
+                       .nr = num_channels,
+                       .ain = AD7124_CHANNEL_AINP(AD7124_INPUT_TEMPSENSOR) |
+                               AD7124_CHANNEL_AINM(AD7124_INPUT_AVSS),
+                       .cfg = {
+                               .bipolar = true,
+                       },
+               };
+
+               chan[num_channels] = (struct iio_chan_spec) {
+                       .type = IIO_TEMP,
+                       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+                               BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) |
+                               BIT(IIO_CHAN_INFO_SAMP_FREQ),
+                       .scan_type = {
+                               /*
+                                * You might find it strange that a bipolar
+                                * measurement yields an unsigned value, but
+                                * this matches the device's manual.
+                                */
+                               .sign = 'u',
+                               .realbits = 24,
+                               .storagebits = 32,
+                               .endianness = IIO_BE,
+                       },
+                       .address = num_channels,
+                       .scan_index = num_channels,
+               };
+       }
+
        return 0;
 }