Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b62c3a11 authored by Anurag Kumar Vulisha's avatar Anurag Kumar Vulisha Committed by Alexandre Belloni
Browse files

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: default avatarAnurag Kumar Vulisha <anuragku@xilinx.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
parent 01dc6992
Loading
Loading
Loading
Loading
+39 −6
Original line number Diff line number Diff line
@@ -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);

	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);