public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org>
To: linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Stefan Popa <stefan.popa@analog.com>,
	Jonathan Cameron <jic23@cam.ac.uk>,
	 Greg Kroah-Hartman <gregkh@suse.de>,
	 Michael Auchter <michael.auchter@ni.com>,
	 Jonathan Cameron <jic23@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>,
	 Michael Hennerich <Michael.Hennerich@analog.com>,
	 Jonathan Cameron <jic23@kernel.org>,
	David Lechner <dlechner@baylibre.com>,
	 Andy Shevchenko <andy@kernel.org>,
	 Rodrigo Alencar <rodrigo.alencar@analog.com>
Subject: [PATCH v6 04/12] iio: dac: ad5686: fix powerdown control on dual-channel devices
Date: Tue, 05 May 2026 13:35:05 +0100	[thread overview]
Message-ID: <20260505-ad5686-fixes-v6-4-c2d5f7be32be@analog.com> (raw)
In-Reply-To: <20260505-ad5686-fixes-v6-0-c2d5f7be32be@analog.com>

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

Fix powerdown control by using a proper bit shift for the powerdown mask
values. During initialization, powerdown bits are initialized so that
unused bits are set to 1 and the correct bit shift is used. Dual-channel
devices use one-hot encoding in the address and that reflects on the
position of the powerdown bits, which are not channel-index based
for that case. Quad-channel devices also use one-hot encoding for the
channel address but the result of log2(address) coincides with the channel
index value. Mask as 0x3U is used rather than 0x3, because shift can reach
value of 30 (last channel of a 16-channel device), which would mess with
the sign bit. The issue was introduced when first adding support for
dual-channel devices, which overlooked powerdown control differences.

Fixes: 7dc8faeab3e3 ("iio: dac: ad5686: add support for AD5338R")
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 drivers/iio/dac/ad5686.c | 40 ++++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 9eb6b61c471d..19440b17645b 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -25,26 +25,37 @@ static const char * const ad5686_powerdown_modes[] = {
 	"three_state"
 };
 
+static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan)
+{
+	if (chan->channel == chan->address)
+		return chan->channel * 2;
+
+	/* one-hot encoding is used in dual/quad channel devices */
+	return __ffs(chan->address) * 2;
+}
+
 static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
 				     const struct iio_chan_spec *chan)
 {
+	unsigned int shift = ad5686_pd_mask_shift(chan);
 	struct ad5686_state *st = iio_priv(indio_dev);
 
 	guard(mutex)(&st->lock);
 
-	return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
+	return ((st->pwr_down_mode >> shift) & 0x3U) - 1;
 }
 
 static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
 				     const struct iio_chan_spec *chan,
 				     unsigned int mode)
 {
+	unsigned int shift = ad5686_pd_mask_shift(chan);
 	struct ad5686_state *st = iio_priv(indio_dev);
 
 	guard(mutex)(&st->lock);
 
-	st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
-	st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
+	st->pwr_down_mode &= ~(0x3U << shift);
+	st->pwr_down_mode |= (mode + 1) << shift;
 
 	return 0;
 }
@@ -59,12 +70,12 @@ static const struct iio_enum ad5686_powerdown_mode_enum = {
 static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
 		uintptr_t private, const struct iio_chan_spec *chan, char *buf)
 {
+	unsigned int shift = ad5686_pd_mask_shift(chan);
 	struct ad5686_state *st = iio_priv(indio_dev);
 
 	guard(mutex)(&st->lock);
 
-	return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask &
-				       (0x3 << (chan->channel * 2))));
+	return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3U << shift)));
 }
 
 static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
@@ -86,9 +97,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
 	guard(mutex)(&st->lock);
 
 	if (readin)
-		st->pwr_down_mask |= (0x3 << (chan->channel * 2));
+		st->pwr_down_mask |= 0x3U << ad5686_pd_mask_shift(chan);
 	else
-		st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
+		st->pwr_down_mask &= ~(0x3U << ad5686_pd_mask_shift(chan));
 
 	switch (st->chip_info->regmap_type) {
 	case AD5310_REGMAP:
@@ -468,7 +479,7 @@ int ad5686_probe(struct device *dev,
 {
 	struct ad5686_state *st;
 	struct iio_dev *indio_dev;
-	unsigned int val, ref_bit_msk;
+	unsigned int val, ref_bit_msk, shift;
 	bool has_external_vref;
 	u8 cmd;
 	int ret, i;
@@ -492,9 +503,18 @@ int ad5686_probe(struct device *dev,
 	has_external_vref = ret != -ENODEV;
 	st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv;
 
+	/* Initialize masks to all ones provided the max shift (last channel) */
+	shift = ad5686_pd_mask_shift(&st->chip_info->channels[st->chip_info->num_channels - 1]);
+	st->pwr_down_mask = GENMASK(shift + 1, 0);
+	st->pwr_down_mode = GENMASK(shift + 1, 0);
+
 	/* Set all the power down mode for all channels to 1K pulldown */
-	for (i = 0; i < st->chip_info->num_channels; i++)
-		st->pwr_down_mode |= (0x01 << (i * 2));
+	for (i = 0; i < st->chip_info->num_channels; i++) {
+		shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]);
+		st->pwr_down_mask &= ~(0x3U << shift); /* powered up state */
+		st->pwr_down_mode &= ~(0x3U << shift);
+		st->pwr_down_mode |= 0x01U << shift;
+	}
 
 	indio_dev->name = name;
 	indio_dev->info = &ad5686_info;

-- 
2.43.0



  parent reply	other threads:[~2026-05-05 12:35 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-05 12:35 [PATCH v6 00/12] Fixes and cleanups for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 01/12] iio: dac: ad5686: fix ref bit initialization for single-channel parts Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 02/12] iio: dac: ad5686: fix input raw value check Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 03/12] iio: dac: ad5686: acquire lock when doing powerdown control Rodrigo Alencar via B4 Relay
2026-05-05 12:58   ` Andy Shevchenko
2026-05-05 14:06     ` Rodrigo Alencar
2026-05-05 15:21       ` Andy Shevchenko
2026-05-06 17:20   ` Jonathan Cameron
2026-05-05 12:35 ` Rodrigo Alencar via B4 Relay [this message]
2026-05-06 17:22   ` [PATCH v6 04/12] iio: dac: ad5686: fix powerdown control on dual-channel devices Jonathan Cameron
2026-05-05 12:35 ` [PATCH v6 05/12] iio: dac: ad5686: refactor include headers Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 06/12] iio: dac: ad5686: remove redundant register definition Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 07/12] iio: dac: ad5686: drop enum id Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 08/12] iio: dac: ad5686: add of_match table to the spi driver Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 09/12] iio: dac: ad5686: add helpers to handle powerdown masks Rodrigo Alencar via B4 Relay
2026-05-05 13:17   ` Andy Shevchenko
2026-05-05 14:13     ` Rodrigo Alencar
2026-05-05 15:27       ` Andy Shevchenko
2026-05-05 15:50         ` Rodrigo Alencar
2026-05-05 16:31           ` Rodrigo Alencar
2026-05-05 12:35 ` [PATCH v6 10/12] iio: dac: ad5686: add control_sync() for single-channel devices Rodrigo Alencar via B4 Relay
2026-05-06 14:22   ` Rodrigo Alencar
2026-05-05 12:35 ` [PATCH v6 11/12] iio: dac: ad5686: cleanup doc header of local structs Rodrigo Alencar via B4 Relay
2026-05-05 12:35 ` [PATCH v6 12/12] iio: dac: ad5686: create bus ops struct Rodrigo Alencar via B4 Relay
2026-05-05 12:59 ` [PATCH v6 00/12] Fixes and cleanups for the AD5686 IIO driver Andy Shevchenko
2026-05-05 14:29   ` Rodrigo Alencar
2026-05-06 17:29     ` Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260505-ad5686-fixes-v6-4-c2d5f7be32be@analog.com \
    --to=devnull+rodrigo.alencar.analog.com@kernel.org \
    --cc=Michael.Hennerich@analog.com \
    --cc=andy@kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=gregkh@suse.de \
    --cc=jic23@cam.ac.uk \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michael.auchter@ni.com \
    --cc=rodrigo.alencar@analog.com \
    --cc=stefan.popa@analog.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox