mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 05:32:00 +00:00
media: rkisp1: Fix IRQ handling due to shared interrupts
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>
This commit is contained in:
parent
a107d643b2
commit
ffb635bb39
@ -725,6 +725,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;
|
||||
|
@ -450,6 +450,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;
|
||||
@ -471,6 +472,7 @@ struct rkisp1_device {
|
||||
struct rkisp1_debug debug;
|
||||
const struct rkisp1_info *info;
|
||||
int irqs[RKISP1_NUM_IRQS];
|
||||
bool irqs_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -196,6 +196,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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -976,6 +976,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;
|
||||
|
Loading…
Reference in New Issue
Block a user