[chrony-dev] [PATCH 1/2] New refclock for RTCs

[ Thread Index | Date Index | More chrony.tuxfamily.org/chrony-dev Archives ]


This refclock uses an RTC as reference source. If the RTC doesn't
support reporting an update event this source is quite coarse as it
usually needs a slow bus access to be read and has a precision of only
one second. If reporting an update event is available, the time is read
just after such an event which improves precision.

Depending on hardware capabilities you might want to combine it with a
PPS reference clock sourced from the same chip.

Note that you can enable UIE emulation in the Linux kernel to make a RTC
without interrupt support look like one with irqs in return for some
system and bus overhead.
---
 configure      |   2 +-
 refclock.c     |   3 +
 refclock_rtc.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 1 deletion(-)
 create mode 100644 refclock_rtc.c

diff --git a/configure b/configure
index 5b8948f0d368..769b534d862d 100755
--- a/configure
+++ b/configure
@@ -517,7 +517,7 @@ fi
 
 if [ $feat_refclock = "1" ]; then
   add_def FEAT_REFCLOCK
-  EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
+  EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o refclock_rtc.o"
 fi
 
 MYCC="$CC"
diff --git a/refclock.c b/refclock.c
index 23a495a1986f..a95f7fd59902 100644
--- a/refclock.c
+++ b/refclock.c
@@ -48,6 +48,7 @@ extern RefclockDriver RCL_SHM_driver;
 extern RefclockDriver RCL_SOCK_driver;
 extern RefclockDriver RCL_PPS_driver;
 extern RefclockDriver RCL_PHC_driver;
+extern RefclockDriver RCL_RTC_driver;
 
 struct FilterSample {
   double offset;
@@ -173,6 +174,8 @@ RCL_AddRefclock(RefclockParameters *params)
     inst->driver = &RCL_PPS_driver;
   } else if (strcmp(params->driver_name, "PHC") == 0) {
     inst->driver = &RCL_PHC_driver;
+  } else if (strcmp(params->driver_name, "RTC") == 0) {
+    inst->driver = &RCL_RTC_driver;
   } else {
     LOG_FATAL("unknown refclock driver %s", params->driver_name);
   }
diff --git a/refclock_rtc.c b/refclock_rtc.c
new file mode 100644
index 000000000000..401ffbac3363
--- /dev/null
+++ b/refclock_rtc.c
@@ -0,0 +1,151 @@
+/*
+  chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) 2021 Uwe Kleine-König, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ **********************************************************************
+
+  =======================================================================
+
+  RTC refclock driver.
+
+  */
+
+#include <linux/rtc.h>
+
+#include "config.h"
+
+#include "refclock.h"
+#include "local.h"
+
+#include "logging.h"
+#include "memory.h"
+#include "sched.h"
+#include "util.h"
+
+struct refrtc_instance {
+  int rtcfd;
+  int polling;
+};
+
+static int refrtc_read_data(RCL_Instance instance, struct refrtc_instance *rtc, double offset)
+{
+  struct rtc_time rtc_t = {0};
+  struct tm rtc_tm = {0};
+  time_t rtc_sec;
+  struct timespec rtc_ts = {0};
+  struct timespec now;
+  int status;
+
+  status = ioctl(rtc->rtcfd, RTC_RD_TIME, &rtc_t);
+  LCL_ReadRawTime(&now);
+  if (status < 0)
+    return 0;
+
+  rtc_tm.tm_sec = rtc_t.tm_sec;
+  rtc_tm.tm_min = rtc_t.tm_min;
+  rtc_tm.tm_hour = rtc_t.tm_hour;
+  rtc_tm.tm_mday = rtc_t.tm_mday;
+  rtc_tm.tm_mon = rtc_t.tm_mon;
+  rtc_tm.tm_year = rtc_t.tm_year;
+
+  rtc_sec = mktime(&rtc_tm);
+  if (rtc_sec == (time_t)-1)
+    return 0;
+
+  status = RCL_AddSample(instance, &now, rtc_sec - now.tv_sec - 1e-9 * now.tv_nsec + offset, LEAP_Normal);
+  if (status == 0) {
+    LOG(LOGS_INFO, "%s:%d: status = %d, rtc_sec = %lu, now=%lu.%09lu",
+	__func__, __LINE__, status, rtc_sec, now.tv_sec, now.tv_nsec);
+  }
+
+  return status;
+}
+
+static void refrtc_read_after_uie(int rtcfd, int event, void *data)
+{
+  RCL_Instance instance = (RCL_Instance)data;
+  struct refrtc_instance *rtc = RCL_GetDriverData(instance);
+  unsigned long rtcdata;
+  ssize_t ret;
+
+  ret = read(rtc->rtcfd, &rtcdata, sizeof(rtcdata));
+  if (ret < sizeof(rtcdata)) {
+    LOG(LOGS_ERR, "%s:%d: reading from RTC returned %zd", __func__, __LINE__, ret);
+    return;
+  }
+
+  if (rtcdata & (RTC_IRQF | RTC_UF) != (RTC_IRQF | RTC_UF))
+    LOG(LOGS_ERR, "unexpected rtc data: 0x%lx", rtcdata);
+  else
+    LOG(LOGS_DEBUG, "read rtc data 0x%lx", rtcdata);
+
+  refrtc_read_data(instance, rtc, 0.0);
+}
+
+static int refrtc_initialise(RCL_Instance instance) {
+  int rtcfd;
+  const char *path;
+  struct refrtc_instance *rtc;
+  int status;
+
+  path = RCL_GetDriverParameter(instance);
+
+  rtcfd = open(path, O_RDONLY);
+  if (rtcfd < 0)
+    LOG_FATAL("Could not open RTC device %s : %m", path);
+
+  rtc = MallocNew(struct refrtc_instance);
+  rtc->rtcfd = rtcfd;
+
+  RCL_SetDriverData(instance, rtc);
+
+  /* Try to enable update interrupts */
+  status = ioctl(rtc->rtcfd, RTC_UIE_ON, 0);
+  if (status == 0) {
+    SCH_AddFileHandler(rtc->rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
+    rtc->polling = 0;
+  } else {
+    LOG(LOGS_INFO, "RTC %p doesn't support update IRQs", path);
+    rtc->polling = 1;
+  }
+
+  return 1;
+}
+
+static void refrtc_finalise(RCL_Instance instance)
+{
+  struct refrtc_instance *rtc;
+
+  rtc = RCL_GetDriverData(instance);
+
+  if (!rtc->polling)
+    ioctl(rtc->rtcfd, RTC_UIE_OFF, 0);
+
+  close(rtc->rtcfd);
+  Free(rtc);
+}
+
+static int refrtc_poll(RCL_Instance instance)
+{
+  struct refrtc_instance *rtc;
+
+  rtc = RCL_GetDriverData(instance);
+
+  if (!rtc->polling)
+    return 0;
+
+  /* As the rtc has a resolution of 1s only add half a second */
+  return refrtc_read_data(instance, rtc, 0.5);
+}
+
+RefclockDriver RCL_RTC_driver = {
+  refrtc_initialise,
+  refrtc_finalise,
+  refrtc_poll
+};
-- 
2.30.2


-- 
To unsubscribe email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "unsubscribe" in the subject.
For help email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "help" in the subject.
Trouble?  Email listmaster@xxxxxxxxxxxxxxxxxxxx.


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/