[PATCH/RFC] rtc: rtc-twl: Fixed nested IRQ handling in resume from suspend

From: Laurent Pinchart
Date: Sat Sep 13 2014 - 07:20:21 EST


The TWL RTC interrupt is a double-nested threaded interrupt, handled
through the TWL SIH (Secondary Interrupt Handler) and PIH (Primary
Interrupt Handler).

When the system is woken up from suspend by a TWL RTC alarm interrupt,
the TWL PIH and SIH are enabled first (due to the normal IRQ enabling
sequence for the PIH and to the IRQF_EARLY_RESUME flag for the SIH)
before the TWL RTC interrupt gets enabled. This results on the interrupt
being processed by the TWL primary interrupt handler, forwarded to the
nested SIH, and then marked as pending for the RTC by handle_nested_irq
called from the SIH.

The RTC interrupt then eventually gets reenabled the kernel, which will
try to call its top half interrupt handler. As the interrupt is a nested
threaded IRQ, its primary handler has been set to the
irq_nested_primary_handler function, which is never supposed to be
called and generates a WARN_ON, without waking the IRQ thread up.

Fix this by setting the IRQF_EARLY_RESUME for the TWL RTC interrupt to
ensure it gets enabled before the parent handlers try to process it.

This is likely a bit of a hack, I have a feeling that a more generic
solution that would fix the problem for all nested threaded IRQs enabled
as a wake up source by enable_irq_wake would be better.

I've noticed that Rafael's "PM / sleep / irq: Do not suspend wakeup
interrupts" patch (which has been reverted) might be related, but it
resulted in an endless stream of I2C errors in the kernel log at resume
time:

[ 189.730682] twl4030: I2C error -13 reading PIH ISR
[ 189.730712] twl: Read failed (mod 1, reg 0x01 count 1)

Given that I'm not that familiar with the IRQ subsystem I would
appreciate some guidance on how to properly solve the problem.

Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
---
drivers/rtc/rtc-twl.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index 1915464..3155af1 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -536,7 +536,8 @@ static int twl_rtc_probe(struct platform_device *pdev)

ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
twl_rtc_interrupt,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT |
+ IRQF_EARLY_RESUME,
dev_name(&rtc->dev), rtc);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ is not free.\n");
--
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/