On Mon, 2018-01-01 at 09:54 +0100, Matthias Apitz wrote:
> El día domingo, diciembre 31, 2017 a las 10:19:50a. m. -0700, Ian Lepore 
> escribió:
> 
> > 
> > > 
> > > I will let the C720 over night under power while sitting in the boot menu,
> > > maybe this will fix the RTC battery issue.
> > > 
> > Last time I worked on RTC stuff, cleaning this up got put on my "to-do
> > some day" list.  I think maybe that day has arrived.
> > 
> > -- Ian
> For the moment we solved the issue by booting some older r28nnnn
> memstick, writing a correct date with ntpdate into the RTC and rebooted
> without poweroff. It seems that the RTC survives even some short
> powercyle.
> 
> The CMOS battery is soldered on the motherboard of the Acer C720, i.e.
> no chance to be replaced.
> 
> The issue must be fixed in FreeBSD, i.e. it should boot even with a
> broken RTC. Should I file a PR for this?
> 
> I'm happy to test any patch for this.
> 
>       matthias
> 

Okay, I've created a pair of patches for this.  The first adds some
common support routines usable by all RTC drivers with BCD hardware.
 The second one converts the atrtc driver to use those routines.  The
common code was tested using an i2c RTC chip, but I don't have an x86
testbed, so the atrtc patch is currently untested (it compiles).

The patches are available in a pair of phabricator reviews, plus I'll
attach them to this mail.  If the list scrubs the attachements, you can
download the patches from the phab urls below, just hit the Actions
button and look for Download Raw Diff.

https://reviews.freebsd.org/D13730
https://reviews.freebsd.org/D13731

-- Ian
Index: sys/kern/subr_clock.c
===================================================================
--- sys/kern/subr_clock.c	(revision 327438)
+++ sys/kern/subr_clock.c	(working copy)
@@ -199,6 +199,52 @@ clock_ct_to_ts(struct clocktime *ct, struct timesp
 	return (0);
 }
 
+int
+clock_bcd_to_ts(struct bcd_clocktime *bct, struct timespec *ts)
+{
+	struct clocktime ct;
+
+	/*
+	 * Year may come in as 2-digit or 4-digit.  clock_ct_to_ts() handles the
+	 * century for 2-digit, but we need to decode the century for 4-digit.
+	 */
+	if (validbcd(bct->year)) {
+		if (bct->year <= 0x99)
+			ct.year = FROMBCD(bct->year);
+		else
+			ct.year = FROMBCD(bct->year >> 8) * 100 +
+			    FROMBCD(bct->year & 0xff);
+	} else
+		ct.year = -1;
+
+	/*
+	 * Ensure that all values are valid BCD numbers, to avoid assertions in
+	 * the BCD-to-binary conversion routines.  clock_ct_to_ts() will further
+	 * validate the field ranges (such as 0 <= min <= 59) during conversion.
+	 */
+	if (!validbcd(bct->sec)  || !validbcd(bct->min) ||
+	    !validbcd(bct->hour) || !validbcd(bct->day) ||
+	    !validbcd(bct->mon)  || ct.year == -1) {
+		if (ct_debug) {
+			printf("clock_bcd_to_ts: bad BCD: "
+			    "[%04x-%02x-%02x %02x:%02x:%02x]\n",
+			    bct->year, bct->mon, bct->day,
+			    bct->hour, bct->min, bct->sec);
+		}
+		return (EINVAL);
+	}
+
+	ct.mon  = FROMBCD(bct->mon);
+	ct.day  = FROMBCD(bct->day);
+	ct.hour = FROMBCD(bct->hour);
+	ct.min  = FROMBCD(bct->min);
+	ct.sec  = FROMBCD(bct->sec);
+	ct.dow  = bct->dow;
+	ct.nsec = bct->nsec;
+
+	return (clock_ct_to_ts(&ct, ts));
+}
+
 void
 clock_ts_to_ct(struct timespec *ts, struct clocktime *ct)
 {
@@ -260,6 +306,23 @@ clock_ts_to_ct(struct timespec *ts, struct clockti
 	    ("seconds %d not in 0-60", ct->sec));
 }
 
+void
+clock_ts_to_bcd(struct timespec *ts, struct bcd_clocktime *bct)
+{
+	struct clocktime ct;
+
+	clock_ts_to_ct(ts, &ct);
+
+	bct->year = TOBCD(ct.year % 100) | (TOBCD(ct.year / 100) << 8);
+	bct->mon  = TOBCD(ct.mon);
+	bct->day  = TOBCD(ct.day);
+	bct->hour = TOBCD(ct.hour);
+	bct->min  = TOBCD(ct.min);
+	bct->sec  = TOBCD(ct.sec);
+	bct->dow  = ct.dow;
+	bct->nsec = ct.nsec;
+}
+
 int
 utc_offset(void)
 {
Index: sys/sys/clock.h
===================================================================
--- sys/sys/clock.h	(revision 327438)
+++ sys/sys/clock.h	(working copy)
@@ -60,9 +60,22 @@ extern int tz_dsttime;
 int utc_offset(void);
 
 /*
- * Structure to hold the values typically reported by time-of-day clocks.
- * This can be passed to the generic conversion functions to be converted
- * to a struct timespec.
+ * Structure to hold the values typically reported by time-of-day clocks,
+ * expressed as binary integers (see below for a BCD version).  This can be
+ * passed to the conversion functions to be converted to/from a struct timespec.
+ *
+ * On input, the year is interpreted as follows:
+ *       0 -   69 = 2000 - 2069
+ *      70 -   99 = 1970 - 1999
+ *     100 -  199 = 2000 - 2099 (Supports hardware "century bit".)
+ *     200 - 1969 = Invalid.
+ *    1970 - 9999 = Full 4-digit century+year.
+ *
+ * The dow field is ignored (not even validated) on input, but is always
+ * populated with day-of-week on output.
+ *
+ * clock_ct_to_ts() returns EINVAL if any values are out of range.  The year
+ * field will always be 4-digit on output.
  */
 struct clocktime {
 	int	year;			/* year (4 digit year) */
@@ -79,6 +92,36 @@ int clock_ct_to_ts(struct clocktime *, struct time
 void clock_ts_to_ct(struct timespec *, struct clocktime *);
 
 /*
+ * Structure to hold the values typically reported by time-of-day clocks,
+ * expressed as BCD.  This can be passed to the conversion functions to be
+ * converted to/from a struct timespec.
+ *
+ * The clock_bcd_to_ts() function interprets the values in the year through sec
+ * fields as BCD numbers, and returns EINVAL if any BCD values are out of range.
+ * After conversion to binary, the values are passed to clock_ct_to_ts() and
+ * undergo further validation as described above.  Year may be 2 or 4-digit BCD,
+ * interpreted as described above.  The nsec field is binary.
+ *
+ * The clock_ts_to_bcd() function converts the timespec to BCD values stored
+ * into year through sec.  The value in year will be 4-digit BCD (e.g.,
+ * 0x2017). The mon through sec values will be 2-digit BCD.  The nsec field will
+ * be binary, and the range of dow makes its binary and BCD values identical.
+ */
+struct bcd_clocktime {
+	int	year;			/* year (4 digit year) */
+	int	mon;			/* month (1 - 12) */
+	int	day;			/* day (1 - 31) */
+	int	hour;			/* hour (0 - 23) */
+	int	min;			/* minute (0 - 59) */
+	int	sec;			/* second (0 - 59) */
+	int	dow;			/* day of week (0 - 6; 0 = Sunday) */
+	long	nsec;			/* nano seconds */
+};
+
+int clock_bcd_to_ts(struct bcd_clocktime *, struct timespec *);
+void clock_ts_to_bcd(struct timespec *, struct bcd_clocktime *);
+
+/*
  * Time-of-day clock functions and flags.  These functions might sleep.
  *
  * clock_register and clock_unregister() do what they say.  Upon return from
Index: sys/x86/isa/atrtc.c
===================================================================
--- sys/x86/isa/atrtc.c	(revision 327438)
+++ sys/x86/isa/atrtc.c	(working copy)
@@ -109,17 +109,6 @@ writertc(int reg, u_char val)
 	RTC_UNLOCK;
 }
 
-static __inline int
-readrtc(int port)
-{
-	int readval;
-
-	readval = rtcin(port);
-	if (readval >= 0 && (readval & 0xf) < 0xa && (readval & 0xf0) < 0xa0)
-		return (bcd2bin(readval));
-	return (0);
-}
-
 static void
 atrtc_start(void)
 {
@@ -169,9 +158,9 @@ atrtc_restore(void)
 static void
 atrtc_set(struct timespec *ts)
 {
-	struct clocktime ct;
+	struct bcd_clocktime bct;
 
-	clock_ts_to_ct(ts, &ct);
+	clock_ts_to_bcd(ts, &bct);
 
 	mtx_lock(&atrtc_time_lock);
 
@@ -178,16 +167,15 @@ atrtc_set(struct timespec *ts)
 	/* Disable RTC updates and interrupts. */
 	writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
 
-	writertc(RTC_SEC, bin2bcd(ct.sec)); 		/* Write back Seconds */
-	writertc(RTC_MIN, bin2bcd(ct.min)); 		/* Write back Minutes */
-	writertc(RTC_HRS, bin2bcd(ct.hour));		/* Write back Hours   */
-
-	writertc(RTC_WDAY, ct.dow + 1);			/* Write back Weekday */
-	writertc(RTC_DAY, bin2bcd(ct.day));		/* Write back Day */
-	writertc(RTC_MONTH, bin2bcd(ct.mon));           /* Write back Month   */
-	writertc(RTC_YEAR, bin2bcd(ct.year % 100));	/* Write back Year    */
+	writertc(RTC_SEC,   bct.sec); 		/* Write back Seconds */
+	writertc(RTC_MIN,   bct.min); 		/* Write back Minutes */
+	writertc(RTC_HRS,   bct.hour);		/* Write back Hours   */
+	writertc(RTC_WDAY,  bct.dow + 1);	/* Write back Weekday */
+	writertc(RTC_DAY,   bct.day);		/* Write back Day */
+	writertc(RTC_MONTH, bct.mon);           /* Write back Month   */
+	writertc(RTC_YEAR,  bct.year & 0xff);	/* Write back Year    */
 #ifdef USE_RTC_CENTURY
-	writertc(RTC_CENTURY, bin2bcd(ct.year / 100));	/* ... and Century    */
+	writertc(RTC_CENTURY, bct.year >> 8);	/* ... and Century    */
 #endif
 
 	/* Re-enable RTC updates and interrupts. */
@@ -351,7 +339,7 @@ atrtc_settime(device_t dev __unused, struct timesp
 static int
 atrtc_gettime(device_t dev, struct timespec *ts)
 {
-	struct clocktime ct;
+	struct bcd_clocktime bct;
 
 	/* Look if we have a RTC present and the time is valid */
 	if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) {
@@ -370,24 +358,21 @@ atrtc_gettime(device_t dev, struct timespec *ts)
 	while (rtcin(RTC_STATUSA) & RTCSA_TUP)
 		continue;
 	critical_enter();
-	ct.nsec = 0;
-	ct.sec = readrtc(RTC_SEC);
-	ct.min = readrtc(RTC_MIN);
-	ct.hour = readrtc(RTC_HRS);
-	ct.day = readrtc(RTC_DAY);
-	ct.dow = readrtc(RTC_WDAY) - 1;
-	ct.mon = readrtc(RTC_MONTH);
-	ct.year = readrtc(RTC_YEAR);
+	bct.sec  = rtcin(RTC_SEC);
+	bct.min  = rtcin(RTC_MIN);
+	bct.hour = rtcin(RTC_HRS);
+	bct.day  = rtcin(RTC_DAY);
+	bct.mon  = rtcin(RTC_MONTH);
+	bct.year = rtcin(RTC_YEAR);
 #ifdef USE_RTC_CENTURY
-	ct.year += readrtc(RTC_CENTURY) * 100;
-#else
-	ct.year += (ct.year < 80 ? 2000 : 1900);
+	bct.year |= rtcin(RTC_CENTURY) << 8;
 #endif
 	critical_exit();
 	mtx_unlock(&atrtc_time_lock);
-	/* Set dow = -1 because some clocks don't set it correctly. */
-	ct.dow = -1;
-	return (clock_ct_to_ts(&ct, ts));
+	/* dow is unused in timespec conversion and we have no nsec info. */
+	bct.dow  = 0;
+	bct.nsec = 0;
+	return (clock_bcd_to_ts(&bct, ts));
 }
 
 static device_method_t atrtc_methods[] = {
_______________________________________________
freebsd-current@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "freebsd-current-unsubscr...@freebsd.org"

Reply via email to