From: Rodrigo Alencar <[email protected]>

This patch fixes powerdown control issues by protecting the cached
powerdown states with mutex access, and 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 encondig 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 encondig for the
channel address but the result of log2(address) coincides with the channel
index value.

Signed-off-by: Rodrigo Alencar <[email protected]>
---
 drivers/iio/dac/ad5686.c | 54 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 4ca9cd5b6e38..19d791c655b7 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -40,12 +40,26 @@ static int ad5683_control_sync(struct ad5686_state *st)
                         FIELD_PREP(AD5683_REF_BIT_MSK, 
!st->use_internal_vref));
 }
 
+static inline 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)
 {
        struct ad5686_state *st = iio_priv(indio_dev);
+       int mode, shift = ad5686_pd_mask_shift(chan);
 
-       return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
+       mutex_lock(&st->lock);
+       mode = ((st->pwr_down_mode >> shift) & 0x3) - 1;
+       mutex_unlock(&st->lock);
+
+       return mode;
 }
 
 static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
@@ -53,9 +67,12 @@ static int ad5686_set_powerdown_mode(struct iio_dev 
*indio_dev,
                                     unsigned int mode)
 {
        struct ad5686_state *st = iio_priv(indio_dev);
+       int shift = ad5686_pd_mask_shift(chan);
 
-       st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
-       st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
+       mutex_lock(&st->lock);
+       st->pwr_down_mode &= ~(0x3 << shift);
+       st->pwr_down_mode |= ((mode + 1) << shift);
+       mutex_unlock(&st->lock);
 
        return 0;
 }
@@ -71,9 +88,13 @@ static ssize_t ad5686_read_dac_powerdown(struct iio_dev 
*indio_dev,
                uintptr_t private, const struct iio_chan_spec *chan, char *buf)
 {
        struct ad5686_state *st = iio_priv(indio_dev);
+       int val, shift = ad5686_pd_mask_shift(chan);
 
-       return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask &
-                                      (0x3 << (chan->channel * 2))));
+       mutex_lock(&st->lock);
+       val = !!(st->pwr_down_mask & (0x3 << shift));
+       mutex_unlock(&st->lock);
+
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
@@ -83,7 +104,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev 
*indio_dev,
                                          size_t len)
 {
        bool readin;
-       int ret;
+       int ret, shift = ad5686_pd_mask_shift(chan);
        struct ad5686_state *st = iio_priv(indio_dev);
        unsigned int val;
        u8 address = 0;
@@ -92,10 +113,12 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev 
*indio_dev,
        if (ret)
                return ret;
 
+       mutex_lock(&st->lock);
+
        if (readin)
-               st->pwr_down_mask |= (0x3 << (chan->channel * 2));
+               st->pwr_down_mask |= (0x3 << shift);
        else
-               st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
+               st->pwr_down_mask &= ~(0x3 << shift);
 
        switch (st->chip_info->regmap_type) {
        case AD5310_REGMAP:
@@ -116,9 +139,10 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev 
*indio_dev,
                ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, address, val);
                break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
        }
 
+       mutex_unlock(&st->lock);
 
        return ret ? ret : len;
 }
@@ -434,7 +458,7 @@ int ad5686_probe(struct device *dev,
 {
        struct iio_dev *indio_dev;
        struct ad5686_state *st;
-       int ret, i;
+       int ret, i, shift;
 
        indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
        if (indio_dev == NULL)
@@ -455,8 +479,14 @@ int ad5686_probe(struct device *dev,
        st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret 
/ 1000;
 
        /* 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));
+       st->pwr_down_mode = ~0U;
+       st->pwr_down_mask = ~0U;
+       for (i = 0; i < st->chip_info->num_channels; i++) {
+               shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]);
+               st->pwr_down_mask &= ~(0x3 << shift); /* powered up state */
+               st->pwr_down_mode &= ~(0x3 << shift);
+               st->pwr_down_mode |= (0x01 << shift);
+       }
 
        indio_dev->name = name;
        indio_dev->info = &ad5686_info;

-- 
2.43.0



Reply via email to