diff options
author | Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com> | 2016-04-20 17:47:35 +0200 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2016-05-20 12:33:51 +0200 |
commit | b62c3a1158c0abc98e03c68934fa7ee6ca10dd49 (patch) | |
tree | 2142a220dfe34610e95b13ff8385aef341a356d3 /drivers/rtc/rtc-zynqmp.c | |
parent | rtc: sa1100: DT spelling s/interrupt-name/interrupt-names/ (diff) | |
download | linux-b62c3a1158c0abc98e03c68934fa7ee6ca10dd49.tar.xz linux-b62c3a1158c0abc98e03c68934fa7ee6ca10dd49.zip |
rtc: zynqmp: Update seconds time programming logic
We program RTC time using SET_TIME_WRITE register and read the RTC
current time using CURRENT_TIME register. When we set the time by
writing into SET_TIME_WRITE Register and immediately try to read the
rtc time from CURRENT_TIME register, the previous old value is
returned instead of the new loaded time. This is because RTC takes
nearly 1 sec to update the new loaded value into the CURRENT_TIME
register. This behaviour is expected in our RTC IP.
This patch updates the driver to read the current time from SET_TIME_WRITE
register instead of CURRENT_TIME when rtc time is requested within an 1sec
period after setting the RTC time. Doing so will ensure the correct time is
given to the user.
Since there is a delay of 1sec in updating the CURRENT_TIME we are loading
set time +1sec while programming the SET_TIME_WRITE register, doing this
will give correct time without any delay when read from CURRENT_TIME.
Signed-off-by: Anurag Kumar Vulisha <anuragku@xilinx.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc/rtc-zynqmp.c')
-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 f87f971a72c2..da18a8ae3c1d 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); |