* patch "iio: dac: ad5686: fix powerdown control on dual-channel devices" added to char-misc-linus
@ 2026-05-15 11:46 gregkh
0 siblings, 0 replies; only message in thread
From: gregkh @ 2026-05-15 11:46 UTC (permalink / raw)
To: rodrigo.alencar, Stable, jic23
This is a note to let you know that I've just added the patch titled
iio: dac: ad5686: fix powerdown control on dual-channel devices
to my char-misc git tree which can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
in the char-misc-linus branch.
The patch will show up in the next release of the linux-next tree
(usually sometime within the next 24 hours during the week.)
The patch will hopefully also be merged in Linus's tree for the
next -rc kernel release.
If you have any questions about this process, please let me know.
From 8aeaf25a85263a7a43357e16ad78ab969f6f8aeb Mon Sep 17 00:00:00 2001
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Date: Tue, 5 May 2026 13:35:05 +0100
Subject: iio: dac: ad5686: fix powerdown control on dual-channel devices
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>
Cc: <Stable@vger.kernel.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
---
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 2e443fcfeb39..a7213bc6b156 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.54.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-15 11:47 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 11:46 patch "iio: dac: ad5686: fix powerdown control on dual-channel devices" added to char-misc-linus gregkh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox