]> git.karo-electronics.de Git - linux-beck.git/commitdiff
iio: adc: xilinx-xadc: Push interrupts into hardirq context
authorXander Huff <xander.huff@ni.com>
Tue, 11 Aug 2015 23:00:49 +0000 (18:00 -0500)
committerJonathan Cameron <jic23@kernel.org>
Sun, 16 Aug 2015 09:51:27 +0000 (10:51 +0100)
The driver currently registers a pair of irq handlers using
request_threaded_irq(), however the synchronization mechanism between the
hardirq and the threadedirq handler is a regular spinlock.

Unfortunately, this breaks PREEMPT_RT builds, where a spinlock can sleep,
and is thus not able to be acquired from a hardirq handler. This patch gets
rid of the threaded handler and pushes all interrupt handling into the
hardirq context, and uses request_irq().

To validate that this change has no impact on RT performance, here are
cyclictest values with no processes running:

$ sudo cyclictest -S -m -p 98
policy: fifo: loadavg: 0.00 0.01 0.05 1/174 2539
T: 0 ( 1405) P:98 I:1000 C:167010520 Min: 9 Act: 12 Avg: 12 Max: 75
T: 1 ( 1862) P:98 I:1500 C:111340339 Min: 9 Act: 12 Avg: 12 Max: 73

Then, all xadc raw handles were accessed in a continuous loop via
/sys/bus/iio/devices/iio:device0:

$ sudo cyclictest -S -m -p 98
policy: fifo: loadavg: 7.84 7.70 7.63 3/182 4260
T: 0 ( 2559) P:98 I:1000 C:241557018 Min: 11 Act: 18 Avg: 21 Max: 74
T: 1 ( 2560) P:98 I:1500 C:161038006 Min: 10 Act: 21 Avg: 20 Max: 73

Signed-off-by: Xander Huff <xander.huff@ni.com>
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/adc/xilinx-xadc-core.c
drivers/iio/adc/xilinx-xadc.h

index ce93bd8e3f68b82fec81b31f8f1885b908ef0b45..0370624a35db723a6b6089c99398cca36fc10590 100644 (file)
@@ -273,33 +273,13 @@ static void xadc_zynq_unmask_worker(struct work_struct *work)
                schedule_delayed_work(&xadc->zynq_unmask_work,
                                msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
        }
-}
-
-static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid)
-{
-       struct iio_dev *indio_dev = devid;
-       struct xadc *xadc = iio_priv(indio_dev);
-       unsigned int alarm;
-
-       spin_lock_irq(&xadc->lock);
-       alarm = xadc->zynq_alarm;
-       xadc->zynq_alarm = 0;
-       spin_unlock_irq(&xadc->lock);
-
-       xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm));
 
-       /* unmask the required interrupts in timer. */
-       schedule_delayed_work(&xadc->zynq_unmask_work,
-                       msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
-
-       return IRQ_HANDLED;
 }
 
 static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
 {
        struct iio_dev *indio_dev = devid;
        struct xadc *xadc = iio_priv(indio_dev);
-       irqreturn_t ret = IRQ_HANDLED;
        uint32_t status;
 
        xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
@@ -321,18 +301,23 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
 
        status &= XADC_ZYNQ_INT_ALARM_MASK;
        if (status) {
-               xadc->zynq_alarm |= status;
                xadc->zynq_masked_alarm |= status;
                /*
                 * mask the current event interrupt,
                 * unmask it when the interrupt is no more active.
                 */
                xadc_zynq_update_intmsk(xadc, 0, 0);
-               ret = IRQ_WAKE_THREAD;
+
+               xadc_handle_events(indio_dev,
+                               xadc_zynq_transform_alarm(status));
+
+               /* unmask the required interrupts in timer. */
+               schedule_delayed_work(&xadc->zynq_unmask_work,
+                               msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
        }
        spin_unlock(&xadc->lock);
 
-       return ret;
+       return IRQ_HANDLED;
 }
 
 #define XADC_ZYNQ_TCK_RATE_MAX 50000000
@@ -437,7 +422,6 @@ static const struct xadc_ops xadc_zynq_ops = {
        .setup = xadc_zynq_setup,
        .get_dclk_rate = xadc_zynq_get_dclk_rate,
        .interrupt_handler = xadc_zynq_interrupt_handler,
-       .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler,
        .update_alarm = xadc_zynq_update_alarm,
 };
 
@@ -1225,9 +1209,8 @@ static int xadc_probe(struct platform_device *pdev)
        if (ret)
                goto err_free_samplerate_trigger;
 
-       ret = request_threaded_irq(irq, xadc->ops->interrupt_handler,
-                               xadc->ops->threaded_interrupt_handler,
-                               0, dev_name(&pdev->dev), indio_dev);
+       ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
+                       dev_name(&pdev->dev), indio_dev);
        if (ret)
                goto err_clk_disable_unprepare;
 
index 54adc5087210bb173afd62bfa36712e4a014eafb..f6f08196564709f1adc18cf0c683811e85fc2a30 100644 (file)
@@ -60,7 +60,6 @@ struct xadc {
 
        enum xadc_external_mux_mode external_mux_mode;
 
-       unsigned int zynq_alarm;
        unsigned int zynq_masked_alarm;
        unsigned int zynq_intmask;
        struct delayed_work zynq_unmask_work;
@@ -79,7 +78,6 @@ struct xadc_ops {
        void (*update_alarm)(struct xadc *, unsigned int);
        unsigned long (*get_dclk_rate)(struct xadc *);
        irqreturn_t (*interrupt_handler)(int, void *);
-       irqreturn_t (*threaded_interrupt_handler)(int, void *);
 
        unsigned int flags;
 };