tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Patch: more than 1 TOD clock



Hi,

Thanks to Andrius Varanavicius for reviewing my original patch and suggesting
improvements!  

> It looks straightforward to support multiple TOD clocks, if we read only
> from the first and write to all of them.  I've done this in the attached
> patch and both TOD clocks are recognised and written to:

I've altered the patch to use SIMPLEQ instead, which makes the code (I hope)
easier to follow.  The main change is that the first TOD reference is now
also allocated dynamically, rather than statically (as in the original).

Comments appreciated.

Thanks and regards,

Julian

-- 
Index: src/sys/kern/kern_todr.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_todr.c,v
retrieving revision 1.52
diff -u -r1.52 kern_todr.c
--- src/sys/kern/kern_todr.c	4 Jan 2026 02:09:03 -0000	1.52
+++ src/sys/kern/kern_todr.c	4 Sep 2026 13:52:55 -0000
@@ -78,7 +78,9 @@
 #include <sys/device_calls.h>
 #include <sys/intr.h>
 #include <sys/kernel.h>
+#include <sys/kmem.h>
 #include <sys/mutex.h>
+#include <sys/queue.h>
 #include <sys/rndsource.h>
 #include <sys/sdt.h>
 #include <sys/systm.h>
@@ -89,8 +91,14 @@
 static int todr_gettime(todr_chip_handle_t, struct timeval *);
 static int todr_settime(todr_chip_handle_t, struct timeval *);
 
+struct todr_entry {
+	todr_chip_handle_t todr_handle;
+	SIMPLEQ_ENTRY(todr_entry) todr_entries;
+};
+static SIMPLEQ_HEAD(, todr_entry) todr_head =
+    SIMPLEQ_HEAD_INITIALIZER(todr_head);
+
 static kmutex_t todr_mutex;
-static todr_chip_handle_t todr_handle;
 static bool todr_initialized;
 
 /* The minimum reasonable RTC date before preposterousness */
@@ -179,6 +187,7 @@
 void
 todr_attach(todr_chip_handle_t todr)
 {
+	struct todr_entry *todrp;
 
 	/*
 	 * todr_init() is called very early in main(), but this is
@@ -191,13 +200,15 @@
 		return;
 	}
 
+	todrp = kmem_alloc(sizeof(struct todr_entry), KM_SLEEP);
 	todr_lock();
-	if (todr_handle) {
-		todr_unlock();
-		printf("todr_attach: TOD already configured\n");
-		return;
+	if (SIMPLEQ_EMPTY(&todr_head)) {
+		SIMPLEQ_INSERT_HEAD(&todr_head, todrp, todr_entries);
+	} else {
+		SIMPLEQ_INSERT_TAIL(&todr_head, todrp, todr_entries);
+		printf("%s: secondary clock\n", device_xname(todr->todr_dev));
 	}
-	todr_handle = todr;
+	todrp->todr_handle = todr;
 	todr_unlock();
 }
 
@@ -217,7 +228,10 @@
 	bool goodtime = false;
 	bool badrtc = false;
 	struct timespec ts;
-	struct timeval tv;
+	struct timeval tv, tv2;
+	struct todr_entry *todrp;
+	device_t t_dev;
+	time_t deltat;
 
 	KASSERT(todr_lock_owned());
 
@@ -246,22 +260,28 @@
 	/*
 	 * Some ports need to be supplied base in order to fabricate a time_t.
 	 */
-	if (todr_handle)
-		todr_handle->todr_base_time = base;
+	if (SIMPLEQ_EMPTY(&todr_head)) {
+		todrp = NULL;
+	} else {
+		todrp = SIMPLEQ_FIRST(&todr_head);
+		if (todrp->todr_handle != NULL)
+			todrp->todr_handle->todr_base_time = base;
+	}
 
 	memset(&tv, 0, sizeof(tv));
 
-	if ((todr_handle == NULL) ||
-	    (todr_gettime(todr_handle, &tv) != 0) ||
+	/* Note, that we use the first RTC for system time. */
+	if ((todrp == NULL) || (todrp->todr_handle == NULL) ||
+	    (todr_gettime(todrp->todr_handle, &tv) != 0) ||
 	    (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) {
 
-		if (todr_handle != NULL)
+		if ((todrp != NULL) && (todrp->todr_handle != NULL))
 			printf("WARNING: preposterous TOD clock time\n");
 		else
 			printf("WARNING: no TOD clock present\n");
 		badrtc = true;
 	} else {
-		time_t deltat = tv.tv_sec - base;
+		deltat = tv.tv_sec - base;
 
 		if (deltat < 0)
 			deltat = -deltat;
@@ -301,6 +321,29 @@
 		tv.tv_usec = 0;
 	}
 
+	/* Check other clocks, if any, against our time */
+	todrp = SIMPLEQ_FIRST(&todr_head);
+	while ((todrp = SIMPLEQ_NEXT(todrp, todr_entries)) != NULL) {
+		if ((todrp->todr_handle == NULL) || 
+		    (todr_gettime(todrp->todr_handle, &tv2) != 0))
+			continue;
+		t_dev = todrp->todr_handle->todr_dev;
+		if (tv2.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR)) {
+			printf("WARNING: preposterous %s clock time\n",
+			    device_xname(t_dev));
+			deltat = 0;
+		} else {
+			deltat = tv.tv_sec - tv2.tv_sec;
+			rnd_add_data(NULL, &tv, sizeof(tv), 0);
+		}
+		if (deltat < -10)
+			printf("WARNING: %s clock lost %" PRId64 " seconds\n",
+			    device_xname(t_dev), deltat);
+		if (deltat > 10)
+			printf("WARNING: %s clock gained %" PRId64 " seconds\n",
+			    device_xname(t_dev), deltat);
+	}
+
 	timeset = true;
 
 	ts.tv_sec = tv.tv_sec;
@@ -315,13 +358,14 @@
 
 /*
  * todr_save_systime:
- *	Save the current system time back to the TOD clock.
+ *	Save the current system time back to the TOD clock(s).
  *	Must be called with the TODR lock held.
  */
 void
 todr_save_systime(void)
 {
 	struct timeval tv;
+	struct todr_entry *todrp;
 
 	KASSERT(todr_lock_owned());
 
@@ -338,9 +382,12 @@
 	if (tv.tv_sec == 0)
 		return;
 
-	if (todr_handle)
-		if (todr_settime(todr_handle, &tv) != 0)
-			printf("Cannot set TOD clock time\n");
+	todrp = SIMPLEQ_FIRST(&todr_head);
+	SIMPLEQ_FOREACH(todrp, &todr_head, todr_entries)
+		if ((todrp->todr_handle != NULL) &&
+		    (todr_settime(todrp->todr_handle, &tv) != 0))
+			printf("Cannot set %s TOD clock time\n",
+			    device_xname(todrp->todr_handle->todr_dev));
 }
 
 /*
Index: src/sys/arch/sparc64/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/conf/GENERIC,v
retrieving revision 1.253
diff -u -r1.253 GENERIC
--- src/sys/arch/sparc64/conf/GENERIC	28 Aug 2026 21:16:34 -0000	1.253
+++ src/sys/arch/sparc64/conf/GENERIC	3 Sep 2026 07:34:58 -0000
@@ -847,6 +847,7 @@
 seeprom*	at iic? addr?	# i2c-at24c64 fru's
 pcagpio* 	at iic? addr?	# V210/V240 GPIO's
 pcf8574io* 	at iic? addr?	# E250 GPIO's
+dsrtc* 		at iic? addr?	# RSC clock found on V210/V240
 
 auxfan* 	at iic? addr?	# iic fan sensor found on SB2500
 


Home | Main Index | Thread Index | Old Index