iio: ad_sigma_delta: Properly handle SPI bus locking vs CS assertion
authorLars-Peter Clausen <lars@metafoo.de>
Tue, 19 Mar 2019 11:37:55 +0000 (13:37 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 31 May 2019 13:46:24 +0000 (06:46 -0700)
[ Upstream commit df1d80aee963480c5c2938c64ec0ac3e4a0df2e0 ]

For devices from the SigmaDelta family we need to keep CS low when doing a
conversion, since the device will use the MISO line as a interrupt to
indicate that the conversion is complete.

This is why the driver locks the SPI bus and when the SPI bus is locked
keeps as long as a conversion is going on. The current implementation gets
one small detail wrong though. CS is only de-asserted after the SPI bus is
unlocked. This means it is possible for a different SPI device on the same
bus to send a message which would be wrongfully be addressed to the
SigmaDelta device as well. Make sure that the last SPI transfer that is
done while holding the SPI bus lock de-asserts the CS signal.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Alexandru Ardelean <Alexandru.Ardelean@analog.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/iio/adc/ad_sigma_delta.c
include/linux/iio/adc/ad_sigma_delta.h

index ae2a509..25af4c7 100644 (file)
@@ -62,7 +62,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
        struct spi_transfer t = {
                .tx_buf         = data,
                .len            = size + 1,
-               .cs_change      = sigma_delta->bus_locked,
+               .cs_change      = sigma_delta->keep_cs_asserted,
        };
        struct spi_message m;
        int ret;
@@ -218,6 +218,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
 
        spi_bus_lock(sigma_delta->spi->master);
        sigma_delta->bus_locked = true;
+       sigma_delta->keep_cs_asserted = true;
        reinit_completion(&sigma_delta->completion);
 
        ret = ad_sigma_delta_set_mode(sigma_delta, mode);
@@ -235,9 +236,10 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
                ret = 0;
        }
 out:
+       sigma_delta->keep_cs_asserted = false;
+       ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
        sigma_delta->bus_locked = false;
        spi_bus_unlock(sigma_delta->spi->master);
-       ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
 
        return ret;
 }
@@ -289,6 +291,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
 
        spi_bus_lock(sigma_delta->spi->master);
        sigma_delta->bus_locked = true;
+       sigma_delta->keep_cs_asserted = true;
        reinit_completion(&sigma_delta->completion);
 
        ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
@@ -298,9 +301,6 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
        ret = wait_for_completion_interruptible_timeout(
                        &sigma_delta->completion, HZ);
 
-       sigma_delta->bus_locked = false;
-       spi_bus_unlock(sigma_delta->spi->master);
-
        if (ret == 0)
                ret = -EIO;
        if (ret < 0)
@@ -316,7 +316,10 @@ out:
                sigma_delta->irq_dis = true;
        }
 
+       sigma_delta->keep_cs_asserted = false;
        ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+       sigma_delta->bus_locked = false;
+       spi_bus_unlock(sigma_delta->spi->master);
        mutex_unlock(&indio_dev->mlock);
 
        if (ret)
@@ -353,6 +356,8 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
 
        spi_bus_lock(sigma_delta->spi->master);
        sigma_delta->bus_locked = true;
+       sigma_delta->keep_cs_asserted = true;
+
        ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
        if (ret)
                goto err_unlock;
@@ -381,6 +386,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
                sigma_delta->irq_dis = true;
        }
 
+       sigma_delta->keep_cs_asserted = false;
        ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
 
        sigma_delta->bus_locked = false;
index 730ead1..57c122a 100644 (file)
@@ -66,6 +66,7 @@ struct ad_sigma_delta {
        bool                    irq_dis;
 
        bool                    bus_locked;
+       bool                    keep_cs_asserted;
 
        uint8_t                 comm;