public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
@ 2026-03-13 10:02 Billy Tsai
  2026-03-13 10:08 ` Andy Shevchenko
  0 siblings, 1 reply; 3+ messages in thread
From: Billy Tsai @ 2026-03-13 10:02 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>
---
 drivers/iio/adc/aspeed_adc.c | 88 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 76 insertions(+), 12 deletions(-)

diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 4be44c524b4d..376a024341e3 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -73,6 +73,31 @@
 #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
+
 #define ASPEED_ADC_INIT_POLLING_TIME	500
 #define ASPEED_ADC_INIT_TIMEOUT		500000
 /*
@@ -121,6 +146,18 @@ 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)
+{
+	/*
+	 * 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;
+}
+
 #define ASPEED_CHAN(_idx, _data_reg_addr) {			\
 	.type = IIO_VOLTAGE,					\
 	.indexed = 1,						\
@@ -281,13 +318,35 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
 			       int *val, int *val2, long mask)
 {
 	struct aspeed_adc_data *data = iio_priv(indio_dev);
-	u32 adc_engine_control_reg_val;
+	u32 adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL);
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		if (data->battery_sensing && chan->channel == 7) {
-			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);
+		}
+
+		/*
+		 * Battery sensing mode requires special configuration:
+		 * - Set channel 7 to battery mode
+		 * - Enable battery sensing functionality
+		 * - Apply voltage divider compensation
+		 */
+		if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) {
 			writel(adc_engine_control_reg_val |
 				       FIELD_PREP(ASPEED_ADC_CH7_MODE,
 						  ASPEED_ADC_CH7_BAT) |
@@ -301,15 +360,15 @@ 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:
-		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
@@ -607,10 +666,15 @@ 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);
+	/*
+	 * Enable channels for normal operation. For battery sensing capable
+	 * controllers, the battery channel is handled separately and is not
+	 * included in the normal channel enable mask.
+	 */
+	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);
 

---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20260313-adc-479b0ab09bae

Best regards,
-- 
Billy Tsai <billy_tsai@aspeedtech.com>



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
  2026-03-13 10:02 [PATCH] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
@ 2026-03-13 10:08 ` Andy Shevchenko
  2026-03-16  2:41   ` Billy Tsai
  0 siblings, 1 reply; 3+ messages in thread
From: Andy Shevchenko @ 2026-03-13 10:08 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 Fri, Mar 13, 2026 at 06:02:16PM +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.
> 
> 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

Why not series of patches each for the change made?

> This ensures optimal power efficiency for normal ADC operations while
> maintaining full functionality when battery sensing is needed.

-- 
With Best Regards,
Andy Shevchenko




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] iio: adc: aspeed: Reserve battery sensing channel for on-demand use
  2026-03-13 10:08 ` Andy Shevchenko
@ 2026-03-16  2:41   ` Billy Tsai
  0 siblings, 0 replies; 3+ messages in thread
From: Billy Tsai @ 2026-03-16  2: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 Fri, Mar 13, 2026 at 06:02:16PM +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.
> >
> > 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

> Why not series of patches each for the change made?

Okay, I will split the changes into a series of patches for better clarity and
review for V2.

Thanks

Billy Tsai

> This ensures optimal power efficiency for normal ADC operations while
> maintaining full functionality when battery sensing is needed.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-03-16  2:42 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13 10:02 [PATCH] iio: adc: aspeed: Reserve battery sensing channel for on-demand use Billy Tsai
2026-03-13 10:08 ` Andy Shevchenko
2026-03-16  2: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