[PATCH v3 2/3] iio: adc: mt6397-auxadc: add mt6397 PMIC AUXADC driver
AngeloGioacchino Del Regno
angelogioacchino.delregno at collabora.com
Mon Sep 21 03:15:34 PDT 2026
On 9/20/26 07:31, 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
> indexes 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 factory-calibrated copy.
>
> 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.
>
> Assisted-by: LLM
> Signed-off-by: Ryan Brue <ryanbrue.dev at gmail.com>
>
> ---
>
> A new driver was created here, instead of modifying an existing driver such
> as mt6323-auxadc or mt6359-auxadc, for the following reasons:
>
> - Both mt6323-auxadc and mt6359-auxadc select channels through a request
> register (1 bit per channel), while mt6397 uses a 4-bit numeric field
> CHSEL in CON1 (10:7), and then pulses a START bit (CON1 bit 0).
>
> - For mt6323-auxadc, which is the closest I could find to the mt6397
> (CON0..CON27), it has 13 more registers than the mt6397 (CON0..CON14).
> It uses CON22 for its request register, and reads the result value
> from the same register as the ready bit. We don't do that - the mt6397
> has a factory-calibrated value for each channel at 0x16 higher than the
> raw value. mt6323 also has a 1800 mV / 15 bit scale / resolution while
> we have 1200 mV / 10 bits. We also have some per-channel preparation
> that we have to do before the burst, that the mt6323 doesn't have to
> do.
>
> - For mt6359-auxadc, it has a more generic framework for describing the
> AUXADC, but it assumes requests are channel-per-bit, and so we would
> have to basically ignore req_idx, req_mask, rdy_idx, and rdy_mask.
>
> - We also have our own software sampling, which the vendor does too
> (Amazon Fire OS based on Linux 3.18). We'd have to have our own
> sampling callback to do it.
> ---
> MAINTAINERS | 1 +
> drivers/iio/adc/Kconfig | 11 ++
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/mt6397-auxadc.c | 391 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 404 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 41d7b3a67415..9b254f98b5d7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -16717,6 +16717,7 @@ MEDIATEK MT6397 PMIC AUXADC DRIVER
> M: Ryan Brue <ryanbrue.dev at gmail.com>
> L: linux-iio at vger.kernel.org
> S: Maintained
> +F: drivers/iio/adc/mt6397-auxadc.c
> F: include/dt-bindings/iio/adc/mediatek,mt6397-auxadc.h
>
> MEDIATEK MT6735 CLOCK & RESET DRIVERS
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 415e519ad4eb..9a1d36ed53cb 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -1233,6 +1233,17 @@ config MEDIATEK_MT6370_ADC
> This driver can also be built as a module. If so, the module
> will be called "mt6370-adc".
>
> +config MEDIATEK_MT6397_AUXADC
> + tristate "MediaTek MT6397 PMIC AUXADC driver"
> + depends on MFD_MT6397
> + help
> + Say yes here to enable support for MediaTek MT6397 PMIC Auxiliary ADC.
> + This driver provides the battery voltage and battery temperature
> + channels used by the fuel gauge on boards built around this PMIC.
> +
> + This driver can also be built as a module. If so, the module will be
> + called mt6397-auxadc.
> +
> config MEDIATEK_MT6577_AUXADC
> tristate "MediaTek AUXADC driver"
> depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index dcec0abb03b7..13cb4206bdb7 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -105,6 +105,7 @@ obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o
> obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o
> obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
> obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o
> +obj-$(CONFIG_MEDIATEK_MT6397_AUXADC) += mt6397-auxadc.o
> obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
> obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
> obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
> diff --git a/drivers/iio/adc/mt6397-auxadc.c b/drivers/iio/adc/mt6397-auxadc.c
> new file mode 100644
> index 000000000000..d860cd3a8922
> --- /dev/null
> +++ b/drivers/iio/adc/mt6397-auxadc.c
> @@ -0,0 +1,391 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * MediaTek MT6397 PMIC AUXADC IIO driver
> + *
> + * Copyright (c) 2026 Ryan Brue <ryanbrue.dev at gmail.com>
> + *
> + * Based on drivers/iio/adc/mt6323-auxadc.c
> + */
> +
..snip..
> +
> +static int mt6397_auxadc_battemp_bias_on(struct mt6397_auxadc *adc)
> +{
> + struct regmap *map = adc->regmap;
> + int ret;
> +
> + 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);
> +}
> +
> +static int mt6397_auxadc_battemp_bias_off(struct mt6397_auxadc *adc)
> +{
> + struct regmap *map = adc->regmap;
> + int ret;
> +
> + ret = regmap_clear_bits(map, MT6397_CHR_CON7, MT6397_CHR_CON7_BATON_TDET_EN);
> + if (ret)
> + return ret;
> +
> + ret = regmap_clear_bits(map, MT6397_AUXADC_CON0, MT6397_AUXADC_CON0_BUF_PWD_B);
> + if (ret)
> + return ret;
If this fails, and the internal buffers are kept enabled, are you sure that
the AUXADC won't lock up?
Should it lock up, I imagine that the best thing that could happen is a power
leakage, but can be as bad as a system lockup.
Should we unconditionally also clear the MT6397_AUXADC_CON0_BUF_PWD_ON bit
even if MT6397_AUXADC_CON0_BUF_PWD_B clearing fails?
> +
> + return regmap_clear_bits(map, MT6397_AUXADC_CON0,
> + MT6397_AUXADC_CON0_BUF_PWD_ON);
> +}
> +
> +static int mt6397_auxadc_isense_enable(struct mt6397_auxadc *adc)
> +{
> + struct regmap *map = adc->regmap;
> + int ret;
> +
> + /*
> + * Two of the five independent input enables, not a select field:
> + * BATSNS is cleared first so that the two inputs are never enabled
> + * onto the battery channel at the same time.
> + */
> + ret = regmap_clear_bits(map, MT6397_CHR_CON16, MT6397_CHR_CON16_ADCIN_VBAT_EN);
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(map, MT6397_CHR_CON16, MT6397_CHR_CON16_ADCIN_VSEN_EN);
> + if (ret)
> + return ret;
> +
> + return regmap_set_bits(map, MT6397_AUXADC_CON14,
> + MT6397_AUXADC_CON14_CH0_NORM_SEL |
> + MT6397_AUXADC_CON14_CH0_LBAT_SEL);
> +}
> +
> +static int mt6397_auxadc_isense_disable(struct mt6397_auxadc *adc)
> +{
> + struct regmap *map = adc->regmap;
> + int ret;
> +
> + ret = regmap_clear_bits(map, MT6397_AUXADC_CON14,
> + MT6397_AUXADC_CON14_CH0_NORM_SEL |
> + MT6397_AUXADC_CON14_CH0_LBAT_SEL);
> + if (ret)
> + return ret;
> +
> + return regmap_clear_bits(map, MT6397_CHR_CON16,
> + MT6397_CHR_CON16_ADCIN_VSEN_EN |
> + MT6397_CHR_CON16_ADCIN_VBAT_EN);
Same here, but makes even more sense: if clearing the channel selector
fails, you still want to disable the ADCIN in an effort to avoid power
leakage.
Everything else looks good, so I'm sure that you're getting my R-b on
version 4.
Cheers,
Angelo
More information about the linux-arm-kernel
mailing list