Re: [PATCH 2/3] iio: adc: mt6397-auxadc: add mt6397 PMIC AUXADC driver

From: Ryan Brue

Date: Thu Sep 17 2026 - 18:21:28 EST


On 9/16/26 4:55 AM, Andy Shevchenko wrote:
On Tue, Sep 15, 2026 at 11:15:27PM -0500, Ryan Brue wrote:
The mt6397 AUXADC is a 10-bit ADC behind the SoC's PMIC wrapper. On boards
built around this PMIC it is the only way to read the battery: the SoC's
AUXADC is wired to board thermistors and the charger ICs these boards use
have no ADC of their own.

Add a driver exposing the battery voltage and battery temperature
channels. Only those two are described, so a channel ID in the device tree
is an index into the driver's channel array rather than the PMIC's channel
number, as mt6323-auxadc does. The ready bit lives in a channel's raw
result register, but the value comes from the chip's trimmed copy of it,
which is what the vendor driver reads for a measurement.

Both channels need more than that, as the vendor programs them. The
battery voltage is measured through ISENSE, because a board with a
switching charger in the power path leaves BATSNS on the charger's system
rail instead of on the pack. The thermistor only reads correctly with the
PMIC's battery-detect bias and input buffer enabled, which take 20 ms to
settle. Both are switched back off afterwards.

Reads average sixteen conversions in software; the chip's sample
accumulator makes no measurable difference at any setting, so it is left
at one sample per conversion.
...

+/*
+ * MediaTek MT6397 PMIC AUXADC IIO driver
+ *
+ * Copyright (c) 2026 Ryan Brue <ryanbrue.dev@xxxxxxxxx>
+ *
+ * Based on drivers/iio/adc/mt6323-auxadc.c
Why not add this device support into that driver?
Please see [1].
+ */
...

+#define MT6397_AUXADC_ISENSE_SETTLE_US USEC_PER_MSEC
(1 * USEC_PER_MSEC)
Ack, fixed in v2
...

+static const struct iio_chan_spec mt6397_auxadc_channels[] = {
+ MTK_PMIC_IIO_CHAN(isense, MT6397_AUXADC_ISENSE,
One space too many.
Ack, fixed in v2
+ MT6397_AUXADC_HWCHAN_BATSNS),
+ MTK_PMIC_IIO_CHAN(bat_temp, MT6397_AUXADC_BAT_TEMP,
+ MT6397_AUXADC_HWCHAN_BAT_TEMP),
+};
...

+static int mt6397_auxadc_battemp_bias(struct mt6397_auxadc *adc, bool on)
+{
+ struct regmap *map = adc->regmap;
+ int ret;
+
+ if (on) {
+ ret = regmap_set_bits(map, MT6397_AUXADC_CON0,
+ MT6397_AUXADC_CON0_BUF_PWD_ON);
+ if (ret)
+ return ret;
+ ret = regmap_set_bits(map, MT6397_AUXADC_CON0,
+ MT6397_AUXADC_CON0_BUF_PWD_B);
+ if (ret)
+ return ret;
+ return regmap_set_bits(map, MT6397_CHR_CON7,
+ MT6397_CHR_CON7_BATON_TDET_EN);
+ }
+
+ ret = regmap_clear_bits(map, MT6397_CHR_CON7,
+ MT6397_CHR_CON7_BATON_TDET_EN);
+ ret = ret ?: regmap_clear_bits(map, MT6397_AUXADC_CON0,
+ MT6397_AUXADC_CON0_BUF_PWD_B);
+ return ret ?: regmap_clear_bits(map, MT6397_AUXADC_CON0,
+ MT6397_AUXADC_CON0_BUF_PWD_ON);
Huh?! Please, use standard pattern with 'if (ret) return ret;'.
Ditto for other weird cases like this.
Fixed in v2, and also modified to allow the regmap clears and sets to fall through, so one failure doesn't leave some of those bits in the wrong state.
+}
...

+{
+ unsigned int i, sum = 0;
+ int ret, sample;
It's preferred not to mix ret with other semantically different variables.
Ack, thanks! Fixed in v2
+ /* Held across the whole burst: the channel select is shared state. */
+ guard(mutex)(&adc->lock);
+
+ if (chan->channel == MT6397_AUXADC_ISENSE) {
+ ret = mt6397_auxadc_isense_enable(adc);
+ if (ret)
+ return ret;
+ fsleep(MT6397_AUXADC_ISENSE_SETTLE_US);
+ } else {
+ ret = mt6397_auxadc_battemp_bias(adc, true);
+ if (ret)
+ return ret;
+ fsleep(MT6397_AUXADC_BATTEMP_SETTLE_US);
+ }
+ for (i = 0; i < MT6397_AUXADC_SAMPLES; i++) {
for (unsigned int i = 0; i < MT6397_AUXADC_SAMPLES; i++) {


+ ret = mt6397_auxadc_read_once(adc, chan, &sample);
+ if (ret)
+ break;
+
+ sum += sample;
+ }
+
+ /* Lower START so the converter is not left armed between reads. */
+ regmap_clear_bits(adc->regmap, MT6397_AUXADC_CON1,
+ MT6397_AUXADC_CON1_START);
+
+ if (chan->channel == MT6397_AUXADC_ISENSE)
+ mt6397_auxadc_isense_disable(adc);
+ else
+ mt6397_auxadc_battemp_bias(adc, false);
+
+ if (ret)
+ return ret;
+
+ *val = DIV_ROUND_CLOSEST(sum, MT6397_AUXADC_SAMPLES);
+
+ return 0;
+}
...

Otherwise nice and small driver.
Thanks for the review, Andy!

Best regards,
Ryan