diff options
-rw-r--r-- | drivers/rtc/rtc-zynqmp.c | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index f87f971..da18a8a 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -64,7 +64,12 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); unsigned long new_time; - new_time = rtc_tm_to_time64(tm); + /* + * The value written will be updated after 1 sec into the + * seconds read register, so we need to program time +1 sec + * to get the correct time on read. + */ + new_time = rtc_tm_to_time64(tm) + 1; if (new_time > RTC_SEC_MAX_VAL) return -EINVAL; @@ -78,14 +83,44 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); + /* + * Clear the rtc interrupt status register after setting the + * time. During a read_time function, the code should read the + * RTC_INT_STATUS register and if bit 0 is still 0, it means + * that one second has not elapsed yet since RTC was set and + * the current time should be read from SET_TIME_READ register; + * otherwise, CURRENT_TIME register is read to report the time + */ + writel(RTC_INT_SEC, xrtcdev->reg_base + RTC_INT_STS); + return 0; } static int xlnx_rtc_read_time(struct device *dev, struct rtc_time *tm) { + u32 status; + unsigned long read_time; struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); - rtc_time64_to_tm(readl(xrtcdev->reg_base + RTC_CUR_TM), tm); + status = readl(xrtcdev->reg_base + RTC_INT_STS); + + if (status & RTC_INT_SEC) { + /* + * RTC has updated the CURRENT_TIME with the time written into + * SET_TIME_WRITE register. + */ + rtc_time64_to_tm(readl(xrtcdev->reg_base + RTC_CUR_TM), tm); + } else { + /* + * Time written in SET_TIME_WRITE has not yet updated into + * the seconds read register, so read the time from the + * SET_TIME_WRITE instead of CURRENT_TIME register. + * Since we add +1 sec while writing, we need to -1 sec while + * reading. + */ + read_time = readl(xrtcdev->reg_base + RTC_SET_TM_RD) - 1; + rtc_time64_to_tm(read_time, tm); + } return rtc_valid_tm(tm); } @@ -166,11 +201,9 @@ static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) if (!(status & (RTC_INT_SEC | RTC_INT_ALRM))) return IRQ_NONE; - /* Clear interrupt */ - writel(status, xrtcdev->reg_base + RTC_INT_STS); + /* Clear RTC_INT_ALRM interrupt only */ + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_STS); - if (status & RTC_INT_SEC) - rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_UF); if (status & RTC_INT_ALRM) rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_AF); |