[PATCH v3] rtc: zynqmp: Add clock framework support with calibration fallback

Tomas Melin tomas.melin at vaisala.com
Wed Jan 21 07:54:44 CET 2026


Hi,

On 20/01/2026 13:00, Pranav Tilak wrote:
> Add support for reading RTC clock from device tree using clock
> framework also update the default calibration value to 0x7FFF
> as per RTC specifications.
> 
> Falls back to 'calibration' property if clock unavailable, and uses
> default calibration if neither is present. Only writes calibration when
> hardware register reads zero.
> 
> The calibration write previously in zynqmp_rtc_set() has been moved to
> the probe function. The earlier implementation wrote calibration on
> every time update to clear the tick counter, but since calibration is
> now dynamically configured from clock framework or device tree during probe,
> it only requires one-time initialization. This avoids repeated tick
> counter resets and unnecessary overhead.
> 
> Signed-off-by: Pranav Tilak <pranav.vinaytilak at amd.com>

Reviewed-by: Tomas Melin <tomas.melin at vaisala.com>

> ---
> Changes in v3:
> - Reorder includes alphabetically
> - Update the commit message
> 
> Changes in v2:
> - Rename variable from clk_freq to calibval
> - Handle case where clock node exists but has no clock-frequency
>   property
> - Treat invalid clock frequency as error instead of warning
> - Move calibration validation outside if-else to apply to all code paths
> 
>  drivers/rtc/zynqmp_rtc.c | 64 +++++++++++++++++++++++++++-------------
>  1 file changed, 44 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/rtc/zynqmp_rtc.c b/drivers/rtc/zynqmp_rtc.c
> index 15122a04838..4fee75bf9cf 100644
> --- a/drivers/rtc/zynqmp_rtc.c
> +++ b/drivers/rtc/zynqmp_rtc.c
> @@ -5,26 +5,30 @@
>  
>  #define LOG_CATEGORY UCLASS_RTC
>  
> +#include <clk.h>
>  #include <dm.h>
>  #include <rtc.h>
>  #include <asm/io.h>
> +#include <dm/device_compat.h>
>  
>  /* RTC Registers */
>  #define RTC_SET_TM_WR		0x00
>  #define RTC_SET_TM_RD		0x04
>  #define RTC_CALIB_WR		0x08
> +#define RTC_CALIB_RD		0x0C
>  #define RTC_CUR_TM		0x10
>  #define RTC_INT_STS		0x20
>  #define RTC_CTRL		0x40
>  
>  #define RTC_INT_SEC		BIT(0)
>  #define RTC_BATT_EN		BIT(31)
> -#define RTC_CALIB_DEF		0x198233
> +#define RTC_CALIB_DEF		0x7FFF
> +#define RTC_FREQ_MAX		0x10000
>  #define RTC_CALIB_MASK		0x1FFFFF
>  
>  struct zynqmp_rtc_priv {
>  	fdt_addr_t	base;
> -	unsigned int	calibval;
> +	unsigned long	calibval;
>  };
>  
>  static int zynqmp_rtc_get(struct udevice *dev, struct rtc_time *tm)
> @@ -70,13 +74,6 @@ static int zynqmp_rtc_set(struct udevice *dev, const struct rtc_time *tm)
>  		 */
>  		new_time = rtc_mktime(tm) + 1;
>  
> -	/*
> -	 * Writing into calibration register will clear the Tick Counter and
> -	 * force the next second to be signaled exactly in 1 second period
> -	 */
> -	priv->calibval &= RTC_CALIB_MASK;
> -	writel(priv->calibval, (priv->base + RTC_CALIB_WR));
> -
>  	writel(new_time, priv->base + RTC_SET_TM_WR);
>  
>  	/*
> @@ -107,15 +104,6 @@ static int zynqmp_rtc_init(struct udevice *dev)
>  	rtc_ctrl |= RTC_BATT_EN;
>  	writel(rtc_ctrl, priv->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.
> -	 */
> -	priv->calibval &= RTC_CALIB_MASK;
> -	writel(priv->calibval, (priv->base + RTC_CALIB_WR));
> -
>  	return 0;
>  }
>  
> @@ -128,8 +116,44 @@ static int zynqmp_rtc_probe(struct udevice *dev)
>  	if (priv->base == FDT_ADDR_T_NONE)
>  		return -EINVAL;
>  
> -	priv->calibval = dev_read_u32_default(dev, "calibration",
> -					      RTC_CALIB_DEF);
> +	ret = readl(priv->base + RTC_CALIB_RD);
> +	if (!ret) {
> +		struct clk rtc_clk;
> +		unsigned long clk_rate;
> +
> +		/* Get the RTC clock rate */
> +		ret = clk_get_by_name_optional(dev, "rtc", &rtc_clk);
> +		if (!ret) {
> +			clk_rate = clk_get_rate(&rtc_clk);
> +			/* Use clock frequency if valid, fallback to calibration value */
> +			if (clk_rate > 0 && clk_rate <= RTC_FREQ_MAX) {
> +				/* Valid clock frequency */
> +				priv->calibval = clk_rate - 1;
> +			} else if (clk_rate == 0) {
> +				priv->calibval = dev_read_u32_default(dev, "calibration",
> +								      RTC_CALIB_DEF);
> +			} else {
> +				dev_err(dev, "Invalid clock frequency 0x%lx\n",
> +					clk_rate);
> +				return -EINVAL;
> +			}
> +		} else {
> +			/* Clock framework unavailable, use DT calibration */
> +			priv->calibval = dev_read_u32_default(dev, "calibration",
> +							      RTC_CALIB_DEF);
> +		}
> +
> +		/* Validate final calibration value */
> +		if (priv->calibval > RTC_FREQ_MAX) {
> +			dev_err(dev, "Invalid calibration 0x%lx\n",
> +				priv->calibval);
> +			return -EINVAL;
> +		}
> +
> +		writel(priv->calibval, (priv->base + RTC_CALIB_WR));
> +	} else {
> +		priv->calibval = ret & RTC_CALIB_MASK;
> +	}
>  
>  	ret = zynqmp_rtc_init(dev);
>  



More information about the U-Boot mailing list