]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
rtc: zynqmp: Add clock framework support with calibration fallback
authorPranav Tilak <pranav.vinaytilak@amd.com>
Tue, 20 Jan 2026 11:00:56 +0000 (16:30 +0530)
committerMichal Simek <michal.simek@amd.com>
Fri, 13 Feb 2026 07:16:24 +0000 (08:16 +0100)
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@amd.com>
Reviewed-by: Tomas Melin <tomas.melin@vaisala.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Link: https://lore.kernel.org/r/20260120110056.3640303-1-pranav.vinaytilak@amd.com
drivers/rtc/zynqmp_rtc.c

index 15122a0483825263ac06f01e441de3739874c75b..4fee75bf9cf5d5ac40df927d2a128b1e33504f7a 100644 (file)
@@ -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);