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)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Thu, 4 Apr 2019 19:21:15 +0000 (20:21 +0100)
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>
drivers/iio/adc/ad_sigma_delta.c
include/linux/iio/adc/ad_sigma_delta.h

index ff5f2da2e1b134d369fbc6ce7180a9ebf0de4ec1..af6cbc6832146dbfd1263b0653540096bf124708 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;
@@ -217,6 +217,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);
@@ -234,9 +235,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)
@@ -321,7 +321,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)
@@ -358,6 +361,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;
@@ -386,6 +391,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 7e84351fa2c0532bea6aa77b0299a2ff4277f345..6e9fb1932dde90978e3a48bc1558b1f5974c9be6 100644 (file)
@@ -69,6 +69,7 @@ struct ad_sigma_delta {
        bool                    irq_dis;
 
        bool                    bus_locked;
+       bool                    keep_cs_asserted;
 
        uint8_t                 comm;