]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
media: rkisp1: Fix IRQ handling due to shared interrupts
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Mon, 18 Dec 2023 07:54:01 +0000 (08:54 +0100)
committerSasha Levin <sashal@kernel.org>
Tue, 26 Mar 2024 22:19:09 +0000 (18:19 -0400)
[ Upstream commit ffb635bb398fc07cb38f8a7b4a82cbe5f412f08e ]

The driver requests the interrupts as IRQF_SHARED, so the interrupt
handlers can be called at any time. If such a call happens while the ISP
is powered down, the SoC will hang as the driver tries to access the
ISP registers.

This can be reproduced even without the platform sharing the IRQ line:
Enable CONFIG_DEBUG_SHIRQ and unload the driver, and the board will
hang.

Fix this by adding a new field, 'irqs_enabled', which is used to bail
out from the interrupt handler when the ISP is not operational.

Link: https://lore.kernel.org/r/20231218-rkisp-shirq-fix-v1-2-173007628248@ideasonboard.com
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-capture.c
drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c

index 8f3cba319762341a9a35167488ccc667fc0862e9..c584bb6d31998335c9a511a5ad36ecc2b2228f99 100644 (file)
@@ -723,6 +723,9 @@ irqreturn_t rkisp1_capture_isr(int irq, void *ctx)
        unsigned int i;
        u32 status;
 
+       if (!rkisp1->irqs_enabled)
+               return IRQ_NONE;
+
        status = rkisp1_read(rkisp1, RKISP1_CIF_MI_MIS);
        if (!status)
                return IRQ_NONE;
index 104a1dbeff4339eb46dfd88bb82c12ed9d736bd3..e9bc6c155d2fcf9a68db0f04efd17aa80c4d1774 100644 (file)
@@ -467,6 +467,7 @@ struct rkisp1_debug {
  * @debug:        debug params to be exposed on debugfs
  * @info:         version-specific ISP information
  * @irqs:          IRQ line numbers
+ * @irqs_enabled:  the hardware is enabled and can cause interrupts
  */
 struct rkisp1_device {
        void __iomem *base_addr;
@@ -488,6 +489,7 @@ struct rkisp1_device {
        struct rkisp1_debug debug;
        const struct rkisp1_info *info;
        int irqs[RKISP1_NUM_IRQS];
+       bool irqs_enabled;
 };
 
 /*
index 0a67eb96402cb7d53c0ea31d664b05dbbefe89c4..1537dccbd2e28f3232baa61df02aa2309898260a 100644 (file)
@@ -211,6 +211,9 @@ irqreturn_t rkisp1_csi_isr(int irq, void *ctx)
        struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
        u32 val, status;
 
+       if (!rkisp1->irqs_enabled)
+               return IRQ_NONE;
+
        status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS);
        if (!status)
                return IRQ_NONE;
index acc559652d6ebae663993105af65e46c9ce5e321..73cf08a740118c05328fdd3f1a9d52a6e935c4e0 100644 (file)
@@ -305,6 +305,24 @@ static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
 {
        struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
 
+       rkisp1->irqs_enabled = false;
+       /* Make sure the IRQ handler will see the above */
+       mb();
+
+       /*
+        * Wait until any running IRQ handler has returned. The IRQ handler
+        * may get called even after this (as it's a shared interrupt line)
+        * but the 'irqs_enabled' flag will make the handler return immediately.
+        */
+       for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) {
+               if (rkisp1->irqs[il] == -1)
+                       continue;
+
+               /* Skip if the irq line is the same as previous */
+               if (il == 0 || rkisp1->irqs[il - 1] != rkisp1->irqs[il])
+                       synchronize_irq(rkisp1->irqs[il]);
+       }
+
        clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks);
        return pinctrl_pm_select_sleep_state(dev);
 }
@@ -321,6 +339,10 @@ static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
        if (ret)
                return ret;
 
+       rkisp1->irqs_enabled = true;
+       /* Make sure the IRQ handler will see the above */
+       mb();
+
        return 0;
 }
 
index 2239fb6c7d393d9c23b54c669dff994f35972f49..8fc9c1c116f1dc2e3c0e6f74d6078a1988b6aa65 100644 (file)
@@ -1027,6 +1027,9 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
        struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
        u32 status, isp_err;
 
+       if (!rkisp1->irqs_enabled)
+               return IRQ_NONE;
+
        status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS);
        if (!status)
                return IRQ_NONE;