diff options
Diffstat (limited to 'drivers/rtc/rtc-zynqmp.c')
| -rw-r--r-- | drivers/rtc/rtc-zynqmp.c | 74 | 
1 files changed, 61 insertions, 13 deletions
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index 8b28762f06df..da18a8ae3c1d 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -45,6 +45,7 @@  #define RTC_INT_SEC		BIT(0)  #define RTC_INT_ALRM		BIT(1)  #define RTC_OSC_EN		BIT(24) +#define RTC_BATT_EN		BIT(31)  #define RTC_CALIB_DEF		0x198233  #define RTC_CALIB_MASK		0x1FFFFF @@ -55,6 +56,7 @@ struct xlnx_rtc_dev {  	void __iomem		*reg_base;  	int			alarm_irq;  	int			sec_irq; +	int			calibval;  };  static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -62,21 +64,63 @@ 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; +	/* +	 * Writing into calibration register will clear the Tick Counter and +	 * force the next second to be signaled exactly in 1 second period +	 */ +	xrtcdev->calibval &= RTC_CALIB_MASK; +	writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); +  	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);  } @@ -120,16 +164,23 @@ static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	return 0;  } -static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev, u32 calibval) +static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)  { +	u32 rtc_ctrl; + +	/* Enable RTC switch to battery when VCC_PSAUX is not available */ +	rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL); +	rtc_ctrl |= RTC_BATT_EN; +	writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL); +  	/*  	 * Based on crystal freq of 33.330 KHz  	 * set the seconds counter and enable, set fractions counter  	 * to default value suggested as per design spec  	 * to correct RTC delay in frequency over period of time.  	 */ -	calibval &= RTC_CALIB_MASK; -	writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); +	xrtcdev->calibval &= RTC_CALIB_MASK; +	writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));  }  static const struct rtc_class_ops xlnx_rtc_ops = { @@ -150,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); @@ -166,7 +215,6 @@ static int xlnx_rtc_probe(struct platform_device *pdev)  	struct xlnx_rtc_dev *xrtcdev;  	struct resource *res;  	int ret; -	unsigned int calibvalue;  	xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL);  	if (!xrtcdev) @@ -207,11 +255,11 @@ static int xlnx_rtc_probe(struct platform_device *pdev)  	}  	ret = of_property_read_u32(pdev->dev.of_node, "calibration", -				   &calibvalue); +				   &xrtcdev->calibval);  	if (ret) -		calibvalue = RTC_CALIB_DEF; +		xrtcdev->calibval = RTC_CALIB_DEF; -	xlnx_init_rtc(xrtcdev, calibvalue); +	xlnx_init_rtc(xrtcdev);  	device_init_wakeup(&pdev->dev, 1);  |