media: rkisp1: Fix IRQ disable race issue
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Thu, 7 Dec 2023 07:57:48 +0000 (08:57 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 5 Feb 2024 20:14:27 +0000 (20:14 +0000)
[ Upstream commit 870565f063a58576e8a4529f122cac4325c6b395 ]

In rkisp1_isp_stop() and rkisp1_csi_disable() the driver masks the
interrupts and then apparently assumes that the interrupt handler won't
be running, and proceeds in the stop procedure. This is not the case, as
the interrupt handler can already be running, which would lead to the
ISP being disabled while the interrupt handler handling a captured
frame.

This brings up two issues: 1) the ISP could be powered off while the
interrupt handler is still running and accessing registers, leading to
board lockup, and 2) the interrupt handler code and the code that
disables the streaming might do things that conflict.

It is not clear to me if 2) causes a real issue, but 1) can be seen with
a suitable delay (or printk in my case) in the interrupt handler,
leading to board lockup.

Link: https://lore.kernel.org/r/20231207-rkisp-irq-fix-v3-4-358a2c871a3c@ideasonboard.com
Tested-by: Adam Ford <aford173@gmail.com> #imx8mp-beacon
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c

index fdff3d0..0a67eb9 100644 (file)
@@ -141,8 +141,20 @@ static void rkisp1_csi_disable(struct rkisp1_csi *csi)
        struct rkisp1_device *rkisp1 = csi->rkisp1;
        u32 val;
 
-       /* Mask and clear interrupts. */
+       /* Mask MIPI interrupts. */
        rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0);
+
+       /* Flush posted writes */
+       rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
+
+       /*
+        * Wait until the IRQ handler has ended. The IRQ handler may get called
+        * even after this, but it will return immediately as the MIPI
+        * interrupts have been masked.
+        */
+       synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MIPI]);
+
+       /* Clear MIPI interrupt status */
        rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);
 
        val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
index 07fbb77..2239fb6 100644 (file)
@@ -281,11 +281,25 @@ static void rkisp1_isp_stop(struct rkisp1_isp *isp)
         * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
         * Stop ISP(isp) ->wait for ISP isp off
         */
-       /* stop and clear MI and ISP interrupts */
-       rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0);
-       rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0);
 
+       /* Mask MI and ISP interrupts */
+       rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0);
        rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0);
+
+       /* Flush posted writes */
+       rkisp1_read(rkisp1, RKISP1_CIF_MI_IMSC);
+
+       /*
+        * Wait until the IRQ handler has ended. The IRQ handler may get called
+        * even after this, but it will return immediately as the MI and ISP
+        * interrupts have been masked.
+        */
+       synchronize_irq(rkisp1->irqs[RKISP1_IRQ_ISP]);
+       if (rkisp1->irqs[RKISP1_IRQ_ISP] != rkisp1->irqs[RKISP1_IRQ_MI])
+               synchronize_irq(rkisp1->irqs[RKISP1_IRQ_MI]);
+
+       /* Clear MI and ISP interrupt status */
+       rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0);
        rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0);
 
        /* stop ISP */