]> www.infradead.org Git - users/hch/misc.git/commitdiff
rtc: zynqmp: Restore alarm functionality after kexec transition
authorHarini T <harini.t@amd.com>
Wed, 30 Jul 2025 14:21:10 +0000 (19:51 +0530)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Sun, 5 Oct 2025 21:17:46 +0000 (23:17 +0200)
During kexec reboots, RTC alarms that are fired during the kernel
transition experience delayed execution. The new kernel would eventually
honor these alarms, but the interrupt handlers would only execute after
the driver probe is completed rather than at the intended alarm time.

This is because pending alarm interrupt status from the previous kernel
is not properly cleared during driver initialization, causing timing
discrepancies in alarm delivery.

To ensure precise alarm timing across kexec transitions, enhance the
probe function to:
1. Clear any pending alarm interrupt status from previous boot.
2. Detect existing valid alarms and preserve their state.
3. Re-enable alarm interrupts for future alarms.

Signed-off-by: Harini T <harini.t@amd.com>
Link: https://lore.kernel.org/r/20250730142110.2354507-1-harini.t@amd.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/rtc-zynqmp.c

index f39102b66eac216cc4465f8dee6dcef7cb0e02b9..3baa2b481d9f2008750046005283b98a0d546c5c 100644 (file)
@@ -277,6 +277,10 @@ static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
 static int xlnx_rtc_probe(struct platform_device *pdev)
 {
        struct xlnx_rtc_dev *xrtcdev;
+       bool is_alarm_set = false;
+       u32 pending_alrm_irq;
+       u32 current_time;
+       u32 alarm_time;
        int ret;
 
        xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL);
@@ -296,6 +300,17 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
        if (IS_ERR(xrtcdev->reg_base))
                return PTR_ERR(xrtcdev->reg_base);
 
+       /* Clear any pending alarm interrupts from previous kernel/boot */
+       pending_alrm_irq = readl(xrtcdev->reg_base + RTC_INT_STS) & RTC_INT_ALRM;
+       if (pending_alrm_irq)
+               writel(pending_alrm_irq, xrtcdev->reg_base + RTC_INT_STS);
+
+       /* Check if a valid alarm is already set from previous kernel/boot */
+       alarm_time = readl(xrtcdev->reg_base + RTC_ALRM);
+       current_time = readl(xrtcdev->reg_base + RTC_CUR_TM);
+       if (alarm_time > current_time && alarm_time != 0)
+               is_alarm_set = true;
+
        xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm");
        if (xrtcdev->alarm_irq < 0)
                return xrtcdev->alarm_irq;
@@ -337,6 +352,10 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
 
        xlnx_init_rtc(xrtcdev);
 
+       /* Re-enable alarm interrupt if a valid alarm was found */
+       if (is_alarm_set)
+               writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN);
+
        device_init_wakeup(&pdev->dev, true);
 
        return devm_rtc_register_device(xrtcdev->rtc);