Commit 4594d082 authored by Srinivas Neeli's avatar Srinivas Neeli Committed by Alexandre Belloni

rtc: zynqmp: Clear alarm interrupt status before interrupt enable

Fix multiple occurring interrupts for alarm interrupt. RTC module doesn't
clear the alarm interrupt status bit immediately after the interrupt is
triggered.This is due to the sticky nature of the alarm interrupt status
register. The alarm interrupt status register can be cleared only after
the second counter outruns the set alarm value. To fix multiple spurious
interrupts, disable alarm interrupt in the handler and clear the status
bit before enabling the alarm interrupt.

Fixes: 11143c19 ("rtc: add xilinx zynqmp rtc driver")
Signed-off-by: default avatarSrinivas Neeli <srinivas.neeli@xilinx.com>
Acked-by: default avatarMichal Simek <michal.simek@xilinx.com>
Link: https://lore.kernel.org/r/1581503079-387-1-git-send-email-srinivas.neeli@xilinx.comSigned-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 38a49742
......@@ -38,6 +38,8 @@
#define RTC_CALIB_DEF 0x198233
#define RTC_CALIB_MASK 0x1FFFFF
#define RTC_ALRM_MASK BIT(1)
#define RTC_MSEC 1000
struct xlnx_rtc_dev {
struct rtc_device *rtc;
......@@ -123,11 +125,28 @@ static int xlnx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int xlnx_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
{
struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
unsigned int status;
ulong timeout;
timeout = jiffies + msecs_to_jiffies(RTC_MSEC);
if (enabled) {
while (1) {
status = readl(xrtcdev->reg_base + RTC_INT_STS);
if (!((status & RTC_ALRM_MASK) == RTC_ALRM_MASK))
break;
if (time_after_eq(jiffies, timeout)) {
dev_err(dev, "Time out occur, while clearing alarm status bit\n");
return -ETIMEDOUT;
}
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_STS);
}
if (enabled)
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN);
else
} else {
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS);
}
return 0;
}
......@@ -183,8 +202,8 @@ static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
if (!(status & (RTC_INT_SEC | RTC_INT_ALRM)))
return IRQ_NONE;
/* Clear RTC_INT_ALRM interrupt only */
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_STS);
/* Disable RTC_INT_ALRM interrupt only */
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS);
if (status & RTC_INT_ALRM)
rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_AF);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment