]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
iio: adc: ad_sigma_delta: Add support for reading irq status using a GPIO
authorUwe Kleine-König <u.kleine-koenig@baylibre.com>
Fri, 6 Dec 2024 17:28:36 +0000 (18:28 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 11 Dec 2024 19:20:48 +0000 (19:20 +0000)
Some of the ADCs by Analog signal their irq condition on the MISO line.
So typically that line is connected to an SPI controller and a GPIO. The
GPIO is used as input and the respective interrupt is enabled when the
last SPI transfer is completed.

Depending on the GPIO controller the toggling MISO line might make the
interrupt pending even while it's masked. In that case the irq handler
is called immediately after irq_enable() and so before the device
actually pulls that line low which results in non-sense values being
reported to the upper layers.

The only way to find out if the line was actually pulled low is to read
the GPIO. (There is a flag in AD7124's status register that also signals
if an interrupt was asserted, but reading that register toggles the MISO
line and so might trigger another spurious interrupt.)

Add the possibility to specify an interrupt GPIO in the machine
description in addition to the plain interrupt. This GPIO is used then
to check if the irq line is actually active in the irq handler.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://patch.msgid.link/5be9a4cc4dc600ec384c88db01dd661a21506b9c.1733504533.git.u.kleine-koenig@baylibre.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 3fd200b34161c592d9a6cc6410e935cbe2ee7282..8fe2ed8b30f9d3f5f44e0b92f2ad499645714cb0 100644 (file)
@@ -539,12 +539,29 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
 {
        struct ad_sigma_delta *sigma_delta = private;
 
-       complete(&sigma_delta->completion);
-       disable_irq_nosync(irq);
-       sigma_delta->irq_dis = true;
-       iio_trigger_poll(sigma_delta->trig);
+       /*
+        * AD7124 and a few others use the same physical line for interrupt
+        * reporting (R̅D̅Y̅) and MISO.
+        * As MISO toggles when reading a register, this likely results in a
+        * pending interrupt. This has two consequences: a) The irq might
+        * trigger immediately after it's enabled even though the conversion
+        * isn't done yet; and b) checking the STATUS register's R̅D̅Y̅ flag is
+        * off-limits as reading that would trigger another irq event.
+        *
+        * So read the MOSI line as GPIO (if available) and only trigger the irq
+        * if the line is active. Without such a GPIO assume this is a valid
+        * interrupt.
+        */
+       if (!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) {
+               complete(&sigma_delta->completion);
+               disable_irq_nosync(irq);
+               sigma_delta->irq_dis = true;
+               iio_trigger_poll(sigma_delta->trig);
 
-       return IRQ_HANDLED;
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
 }
 
 /**
@@ -679,6 +696,17 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
        else
                sigma_delta->irq_line = spi->irq;
 
+       sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN);
+       if (IS_ERR(sigma_delta->rdy_gpiod))
+               return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod),
+                                    "Failed to find rdy gpio\n");
+
+       if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) {
+               sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod);
+               if (sigma_delta->irq_line < 0)
+                       return sigma_delta->irq_line;
+       }
+
        iio_device_set_drvdata(indio_dev, sigma_delta);
 
        return 0;
index 1851f8fed3a44adfe24bbfe2d0d69d68b114b2a9..895b7ebf4be5ef3779de13440c3b9ee58c25e46a 100644 (file)
@@ -29,6 +29,7 @@ struct ad_sd_calib_data {
 
 struct ad_sigma_delta;
 struct device;
+struct gpio_desc;
 struct iio_dev;
 
 /**
@@ -96,6 +97,7 @@ struct ad_sigma_delta {
        unsigned int            active_slots;
        unsigned int            current_slot;
        unsigned int            num_slots;
+       struct gpio_desc        *rdy_gpiod;
        int                     irq_line;
        bool                    status_appended;
        /* map slots to channels in order to know what to expect from devices */