* [PATCH v2 0/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
@ 2026-03-16 3:00 Billy Tsai
2026-03-16 3:00 ` [PATCH v2 1/3] iio: adc: Add battery channel definition for ADC Billy Tsai
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Billy Tsai @ 2026-03-16 3:00 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery
Cc: linux-iio, linux-arm-kernel, linux-aspeed, linux-kernel,
morris_mao, Billy Tsai
For controllers with battery sensing capability (AST2600/AST2700), the last
channel uses a different circuit design optimized for battery voltage
measurement. This channel should not be enabled by default along with other
channels to avoid potential interference and power efficiency issues.
Changes made:
- Introduce aspeed_adc_get_active_channels() to return the number of
channels that should be enabled by default
- For battery sensing capable controllers, exclude the last channel
from the default channel enable mask
- Enable the battery sensing channel only when explicitly accessed
via read_raw()
- Replace hardcoded channel numbers with ASPEED_ADC_BATTERY_CHANNEL macro
- Add helper functions for cleaner channel management
This ensures optimal power efficiency for normal ADC operations while
maintaining full functionality when battery sensing is needed.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
Changes in v2:
- Split the changes into a series of patches for better clarity and
review
- Link to v1: https://lore.kernel.org/r/20260313-adc-v1-1-7a2edb4e5664@aspeedtech.com
---
Billy Tsai (3):
iio: adc: Add battery channel definition for ADC
iio: adc: Enable multiple consecutive channels based on model data
iio: adc: aspeed: Reserve battery sensing channel for on-demand use
drivers/iio/adc/aspeed_adc.c | 75 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 65 insertions(+), 10 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20260313-adc-479b0ab09bae
Best regards,
--
Billy Tsai <billy_tsai@aspeedtech.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/3] iio: adc: Add battery channel definition for ADC
2026-03-16 3:00 [PATCH v2 0/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
@ 2026-03-16 3:00 ` Billy Tsai
2026-03-16 3:00 ` [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data Billy Tsai
2026-03-16 3:00 ` [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2 siblings, 0 replies; 8+ messages in thread
From: Billy Tsai @ 2026-03-16 3:00 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery
Cc: linux-iio, linux-arm-kernel, linux-aspeed, linux-kernel,
morris_mao, Billy Tsai
Defines a constant for the battery sensing channel, typically the last
channel of the ADC. Clarifies channel usage and improves code
readability.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
drivers/iio/adc/aspeed_adc.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 4be44c524b4d..af9a95d31d81 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -72,6 +72,8 @@
#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
+/* Battery sensing is typically on the last channel */
+#define ASPEED_ADC_BATTERY_CHANNEL 7
#define ASPEED_ADC_INIT_POLLING_TIME 500
#define ASPEED_ADC_INIT_TIMEOUT 500000
@@ -285,7 +287,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (data->battery_sensing && chan->channel == 7) {
+ if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) {
adc_engine_control_reg_val =
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
writel(adc_engine_control_reg_val |
@@ -309,7 +311,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
- if (data->battery_sensing && chan->channel == 7)
+ if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL)
*val = (data->cv * data->battery_mode_gain.mult) /
data->battery_mode_gain.div;
else
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data
2026-03-16 3:00 [PATCH v2 0/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2026-03-16 3:00 ` [PATCH v2 1/3] iio: adc: Add battery channel definition for ADC Billy Tsai
@ 2026-03-16 3:00 ` Billy Tsai
2026-03-16 14:25 ` Andy Shevchenko
2026-03-16 3:00 ` [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2 siblings, 1 reply; 8+ messages in thread
From: Billy Tsai @ 2026-03-16 3:00 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery
Cc: linux-iio, linux-arm-kernel, linux-aspeed, linux-kernel,
morris_mao, Billy Tsai
Add helpers to generate channel masks and enable multiple ADC channels
according to the device model's channel count.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
drivers/iio/adc/aspeed_adc.c | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index af9a95d31d81..81a2dd752541 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -72,6 +72,29 @@
#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
+
+/*
+ * Enable multiple consecutive channels starting from channel 0.
+ * This creates a bitmask for channels 0 to (num_channels - 1).
+ * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2)
+ */
+static inline u32 aspeed_adc_channels_mask(unsigned int num_channels)
+{
+ if (num_channels == 0)
+ return 0;
+ if (num_channels >= 16)
+ return GENMASK(15, 0);
+ return GENMASK(num_channels - 1, 0);
+}
+
+/*
+ * Helper function to enable multiple channels in the control register
+ */
+static inline u32 aspeed_adc_enable_channels(unsigned int num_channels)
+{
+ return FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, aspeed_adc_channels_mask(num_channels));
+}
+
/* Battery sensing is typically on the last channel */
#define ASPEED_ADC_BATTERY_CHANNEL 7
@@ -123,6 +146,11 @@ struct aspeed_adc_data {
struct adc_gain battery_mode_gain;
};
+static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data)
+{
+ return data->model_data->num_channels;
+}
+
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@@ -610,9 +638,10 @@ static int aspeed_adc_probe(struct platform_device *pdev)
aspeed_adc_compensation(indio_dev);
/* Start all channels in normal mode. */
- adc_engine_control_reg_val =
- readl(data->base + ASPEED_REG_ENGINE_CONTROL);
- adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
+ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |=
+ aspeed_adc_enable_channels(aspeed_adc_get_active_channels(data));
+
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
2026-03-16 3:00 [PATCH v2 0/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2026-03-16 3:00 ` [PATCH v2 1/3] iio: adc: Add battery channel definition for ADC Billy Tsai
2026-03-16 3:00 ` [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data Billy Tsai
@ 2026-03-16 3:00 ` Billy Tsai
2026-03-16 14:26 ` Andy Shevchenko
2 siblings, 1 reply; 8+ messages in thread
From: Billy Tsai @ 2026-03-16 3:00 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery
Cc: linux-iio, linux-arm-kernel, linux-aspeed, linux-kernel,
morris_mao, Billy Tsai
For controllers with battery sensing capability (AST2600/AST2700), the
last channel uses a different circuit design optimized for battery
voltage measurement. This channel should not be enabled by default
along with other channels to avoid potential interference and power
efficiency issues.
This ensures optimal power efficiency for normal ADC operations while
maintaining full functionality when battery sensing is needed.
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
drivers/iio/adc/aspeed_adc.c | 34 +++++++++++++++++++++++++++++-----
1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 81a2dd752541..ab173d8542c6 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -148,6 +148,13 @@ struct aspeed_adc_data {
static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data)
{
+ /*
+ * For controllers with battery sensing capability, the last channel
+ * is reserved for battery sensing and should not be included in
+ * normal channel operations.
+ */
+ if (data->model_data->bat_sense_sup)
+ return data->model_data->num_channels - 1;
return data->model_data->num_channels;
}
@@ -315,9 +322,26 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * For battery sensing capable controllers, we need to enable
+ * the specific channel before reading. This is required because
+ * the battery channel may not be enabled by default.
+ */
+ if (data->model_data->bat_sense_sup &&
+ chan->channel == ASPEED_ADC_BATTERY_CHANNEL) {
+ u32 ctrl_reg = adc_engine_control_reg_val & ~ASPEED_ADC_CTRL_CHANNEL;
+
+ ctrl_reg |= ASPEED_ADC_CTRL_CHANNEL_ENABLE(chan->channel);
+ writel(ctrl_reg, data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable a new channel need to wait some time for adc stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+ }
+
if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) {
- adc_engine_control_reg_val =
- readl(data->base + ASPEED_REG_ENGINE_CONTROL);
writel(adc_engine_control_reg_val |
FIELD_PREP(ASPEED_ADC_CH7_MODE,
ASPEED_ADC_CH7_BAT) |
@@ -331,11 +355,11 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
*val = readw(data->base + chan->address);
*val = (*val * data->battery_mode_gain.mult) /
data->battery_mode_gain.div;
- /* Restore control register value */
- writel(adc_engine_control_reg_val,
- data->base + ASPEED_REG_ENGINE_CONTROL);
} else
*val = readw(data->base + chan->address);
+ /* Restore control register value */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data
2026-03-16 3:00 ` [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data Billy Tsai
@ 2026-03-16 14:25 ` Andy Shevchenko
2026-03-20 3:33 ` Billy Tsai
0 siblings, 1 reply; 8+ messages in thread
From: Andy Shevchenko @ 2026-03-16 14:25 UTC (permalink / raw)
To: Billy Tsai
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery, linux-iio, linux-arm-kernel,
linux-aspeed, linux-kernel, morris_mao
On Mon, Mar 16, 2026 at 11:00:47AM +0800, Billy Tsai wrote:
> Add helpers to generate channel masks and enable multiple ADC channels
> according to the device model's channel count.
...
> +
(1)
> +/*
> + * Enable multiple consecutive channels starting from channel 0.
> + * This creates a bitmask for channels 0 to (num_channels - 1).
> + * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2)
> + */
> +static inline u32 aspeed_adc_channels_mask(unsigned int num_channels)
> +{
> + if (num_channels == 0)
> + return 0;
> + if (num_channels >= 16)
> + return GENMASK(15, 0);
> + return GENMASK(num_channels - 1, 0);
This entire function can be folded into
return BIT(min(num_channels, 16U)) - 1;
Or
if (num_channels > 16)
return GENMASK(15, 0);
return BIT(num_channels) - 1;
> +}
> +/*
> + * Helper function to enable multiple channels in the control register
> + */
> +static inline u32 aspeed_adc_enable_channels(unsigned int num_channels)
> +{
> + return FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, aspeed_adc_channels_mask(num_channels));
> +}
> +
One of these (see 1 above) new blank lines should be rather added in the previous patch.
> /* Battery sensing is typically on the last channel */
> #define ASPEED_ADC_BATTERY_CHANNEL 7
...
> /* Start all channels in normal mode. */
> - adc_engine_control_reg_val =
> - readl(data->base + ASPEED_REG_ENGINE_CONTROL);
> - adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
> + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL);
> + adc_engine_control_reg_val |=
> + aspeed_adc_enable_channels(aspeed_adc_get_active_channels(data));
Why not FIELD_MODIFY()?
> writel(adc_engine_control_reg_val,
> data->base + ASPEED_REG_ENGINE_CONTROL);
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
2026-03-16 3:00 ` [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
@ 2026-03-16 14:26 ` Andy Shevchenko
2026-03-20 3:41 ` Billy Tsai
0 siblings, 1 reply; 8+ messages in thread
From: Andy Shevchenko @ 2026-03-16 14:26 UTC (permalink / raw)
To: Billy Tsai
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery, linux-iio, linux-arm-kernel,
linux-aspeed, linux-kernel, morris_mao
On Mon, Mar 16, 2026 at 11:00:48AM +0800, Billy Tsai wrote:
> For controllers with battery sensing capability (AST2600/AST2700), the
> last channel uses a different circuit design optimized for battery
> voltage measurement. This channel should not be enabled by default
> along with other channels to avoid potential interference and power
> efficiency issues.
> This ensures optimal power efficiency for normal ADC operations while
> maintaining full functionality when battery sensing is needed.
...
> + /*
> + * After enable a new channel need to wait some time for adc stable
ADC
> + * Experiment result is 1ms.
> + */
> + mdelay(1);
Why atomic? If not required, use fsleep(). Otherwise explain.
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data
2026-03-16 14:25 ` Andy Shevchenko
@ 2026-03-20 3:33 ` Billy Tsai
0 siblings, 0 replies; 8+ messages in thread
From: Billy Tsai @ 2026-03-20 3:33 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery, linux-iio@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
Morris Mao
On Mon, Mar 16, 2026 at 11:00:47AM +0800, Billy Tsai wrote:
> > Add helpers to generate channel masks and enable multiple ADC channels
> > according to the device model's channel count.
> ...
> > +
> (1)
> > +/*
> > + * Enable multiple consecutive channels starting from channel 0.
> > + * This creates a bitmask for channels 0 to (num_channels - 1).
> > + * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2)
> > + */
> > +static inline u32 aspeed_adc_channels_mask(unsigned int num_channels)
> > +{
> > + if (num_channels == 0)
> > + return 0;
> > + if (num_channels >= 16)
> > + return GENMASK(15, 0);
> > + return GENMASK(num_channels - 1, 0);
> This entire function can be folded into
>
> return BIT(min(num_channels, 16U)) - 1;
>
> Or
>
> if (num_channels > 16)
> return GENMASK(15, 0);
>
> return BIT(num_channels) - 1;
Got it. I will replace it to later one.
> > +}
> > +/*
> > + * Helper function to enable multiple channels in the control register
> > + */
> > +static inline u32 aspeed_adc_enable_channels(unsigned int num_channels)
> > +{
> > + return FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, aspeed_adc_channels_mask(num_channels));
> > +}
> > +
> One of these (see 1 above) new blank lines should be rather added in the previous patch.
Good catch, I’ll avoid this blank lines problem.
> > /* Battery sensing is typically on the last channel */
> > #define ASPEED_ADC_BATTERY_CHANNEL 7
...
> > /* Start all channels in normal mode. */
> > - adc_engine_control_reg_val =
> > - readl(data->base + ASPEED_REG_ENGINE_CONTROL);
> > - adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
> > + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL);
> > + adc_engine_control_reg_val |=
> > + aspeed_adc_enable_channels(aspeed_adc_get_active_channels(data));
> Why not FIELD_MODIFY()?
Use FIELD_MODIFY() for the channel field update is more better.
> > writel(adc_engine_control_reg_val,
> > data->base + ASPEED_REG_ENGINE_CONTROL);
Thanks
Billy Tsai
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
2026-03-16 14:26 ` Andy Shevchenko
@ 2026-03-20 3:41 ` Billy Tsai
0 siblings, 0 replies; 8+ messages in thread
From: Billy Tsai @ 2026-03-20 3:41 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Joel Stanley, Andrew Jeffery, linux-iio@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
Morris Mao
On Mon, Mar 16, 2026 at 11:00:48AM +0800, Billy Tsai wrote:
> > For controllers with battery sensing capability (AST2600/AST2700), the
> > last channel uses a different circuit design optimized for battery
> > voltage measurement. This channel should not be enabled by default
> > along with other channels to avoid potential interference and power
> > efficiency issues.
> > This ensures optimal power efficiency for normal ADC operations while
> > maintaining full functionality when battery sensing is needed.
>...
> > + /*
> > + * After enable a new channel need to wait some time for adc stable
> ADC
Got it.
> > + * Experiment result is 1ms.
> > + */
> > + mdelay(1);
> Why atomic? If not required, use fsleep(). Otherwise explain.
Good point.
This path is sleepable, so mdelay() is not required. I’ll switch it to
fsleep(1000) in v2.
I’ll also take a look at other mdelay() users in the driver and clean them
up in a separate patch if appropriate.
> > + }
Thanks
Billy Tsai
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-20 3:41 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-16 3:00 [PATCH v2 0/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2026-03-16 3:00 ` [PATCH v2 1/3] iio: adc: Add battery channel definition for ADC Billy Tsai
2026-03-16 3:00 ` [PATCH v2 2/3] iio: adc: Enable multiple consecutive channels based on model data Billy Tsai
2026-03-16 14:25 ` Andy Shevchenko
2026-03-20 3:33 ` Billy Tsai
2026-03-16 3:00 ` [PATCH v2 3/3] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2026-03-16 14:26 ` Andy Shevchenko
2026-03-20 3:41 ` Billy Tsai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox