Subject: Re: sh3 todr and timecounter conversion (rev2)
To: None <garrett_damore@tadpole.com>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: port-sh3
Date: 09/17/2006 04:56:51
garrett_damore@tadpole.com wrote:

> I just posted revision 2 of this patch, which adjusts for the Valeriy's
> dreamcast changes, and does some other cleanup.  Notably, this patch
> makes the landisk rtc a read device (rs5c313rtc), which allows us to
> remove the rtc_ops structure from the sh_clock_init.

Hmm, landisk now uses the MI rtc driver so there are some conflicts..
(though fix should be trivial)

> Again, I've not tested any of this code, so I'd be grateful if someone
> could.  I'm especially interested in how the timecounters and soft
> interrupts work -- to support timecounters I merged the two level soft
> interrupt scheme and am now using the clock that freed up for microtime
> counting.

Yes, I also thought if we could use one of tmu1 and tmu2 for other
purpose. In my case it was statclock(9), but it's also fine for
timecounter(9) as your patch does.

Note we have to explicitly start tmu2 counter in cpu_initclocks(),
but otherwise your patch seems to work fine on my dreamcast.
(diff against clock.c attached)
---
Izumi Tsutsui


Index: sys/arch/sh3/sh3/clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/clock.c,v
retrieving revision 1.32
diff -u -r1.32 clock.c
--- sys/arch/sh3/sh3/clock.c	5 Sep 2006 11:09:36 -0000	1.32
+++ sys/arch/sh3/sh3/clock.c	16 Sep 2006 19:47:42 -0000
@@ -47,6 +47,7 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/device.h>
+#include <sys/timetc.h>
 
 #include <dev/clock_subr.h>
 
@@ -65,6 +66,9 @@
 #define	MINYEAR		2002	/* "today" */
 #define	SH_RTC_CLOCK	16384	/* Hz */
 
+static int sh_rtc_gettime(todr_chip_handle_t, volatile struct timeval *);
+static int sh_rtc_settime(todr_chip_handle_t, volatile struct timeval *);
+
 /*
  * NetBSD/sh3 clock module
  *  + default 64Hz
@@ -79,9 +83,10 @@
 	uint32_t cpucycle_1us;	/* calibrated loop variable (1 us) */
 	uint32_t tmuclk;	/* source clock of TMU0 (Hz) */
 
+	struct timecounter tc;
+
 	/* RTC ops holder. default SH RTC module */
-	struct rtc_ops rtc;
-	int rtc_initialized;
+	struct todr_chip_handle todr;
 
 	uint32_t pclock;	/* PCLOCK */
 	uint32_t cpuclock;	/* CPU clock */
@@ -90,11 +95,10 @@
 #ifdef PCLOCK
 	.pclock = PCLOCK,
 #endif
-	.rtc = {
-		/* SH RTC module to default RTC */
-		.init	= sh_rtc_init,
-		.get	= sh_rtc_get,
-		.set	= sh_rtc_set
+	.todr = {
+		.cookie = NULL,
+		.todr_gettime = sh_rtc_gettime,
+		.todr_settime = sh_rtc_settime,
 	}
 };
 
@@ -104,6 +108,7 @@
 /* interrupt handler is timing critical. prepared for each. */
 int sh3_clock_intr(void *);
 int sh4_clock_intr(void *);
+unsigned sh_timecounter_get(struct timecounter *);
 
 /*
  * Estimate CPU and Peripheral clock.
@@ -117,13 +122,11 @@
 #define	TMU_ELAPSED(x)							\
 	(0xffffffff - _reg_read_4(SH_(TCNT ## x)))
 void
-sh_clock_init(int flags, struct rtc_ops *rtc)
+sh_clock_init(int flags)
 {
 	uint32_t s, t0, t1 __attribute__((__unused__));
 
 	sh_clock.flags = flags;
-	if (rtc != NULL)
-		sh_clock.rtc = *rtc;	/* structure copy */
 
 	/* Initialize TMU */
 	_reg_write_2(SH_(TCR0), 0);
@@ -208,34 +211,10 @@
 	/* XXX not yet */
 }
 
-/*
- * Return the best possible estimate of the time in the timeval to
- * which tv points.
- */
-void
-microtime(struct timeval *tv)
+unsigned
+sh_timecounter_get(struct timecounter *tc)
 {
-	static struct timeval lasttime;
-	int s;
-
-	s = splclock();
-	*tv = time;
-	splx(s);
-
-	tv->tv_usec += ((sh_clock.hz_cnt - _reg_read_4(SH_(TCNT0)))
-	    * 1000000) / sh_clock.tmuclk;
-	while (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
-	}
-
-	if (tv->tv_sec == lasttime.tv_sec &&
-	    tv->tv_usec <= lasttime.tv_usec &&
-	    (tv->tv_usec = lasttime.tv_usec + 1) >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
-	}
-	lasttime = *tv;
+	return (0xffffffff - _reg_read_4(SH_(TCNT2)));
 }
 
 /*
@@ -286,92 +265,34 @@
 	_reg_bset_1(SH_(TSTR), TSTR_STR0);
 
 	/*
-	 * TMU channel 1, 2 are one shot timer.
+	 * TMU channel 1 is one shot timer for softintr(9).
 	 */
 	_reg_write_2(SH_(TCR1), TCR_UNIE | TCR_TPSC_P4);
 	_reg_write_4(SH_(TCOR1), 0xffffffff);
-	_reg_write_2(SH_(TCR2), TCR_UNIE | TCR_TPSC_P4);
+
+	/*
+	 * TMU channel 2 is freerun counter for timecounter(9).
+	 */
+	_reg_write_2(SH_(TCR2), TCR_TPSC_P4);
 	_reg_write_4(SH_(TCOR2), 0xffffffff);
+	/* start timecounter */
+	_reg_bset_1(SH_(TSTR), TSTR_STR2);
 
 	/* Make sure to start RTC */
-	sh_clock.rtc.init(sh_clock.rtc._cookie);
-}
-
-
-#if !defined(__HAVE_GENERIC_TODR)
-
-void
-inittodr(time_t base)
-{
-	struct clock_ymdhms dt;
-	time_t rtc;
-	int s;
-
-	if (!sh_clock.rtc_initialized)
-		sh_clock.rtc_initialized = 1;
-
-	sh_clock.rtc.get(sh_clock.rtc._cookie, base, &dt);
-	rtc = clock_ymdhms_to_secs(&dt);
-
-#ifdef DEBUG
-	printf("inittodr: %d/%d/%d/%d/%d/%d(%d)\n", dt.dt_year,
-	    dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
-	    dt.dt_wday);
-#endif
-
-	if (!(sh_clock.flags & SH_CLOCK_NOINITTODR) &&
-	    (rtc < base ||
-		dt.dt_year < MINYEAR || dt.dt_year > 2037 ||
-		dt.dt_mon < 1 || dt.dt_mon > 12 ||
-		dt.dt_wday > 6 ||
-		dt.dt_day < 1 || dt.dt_day > 31 ||
-		dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 59)) {
-		/*
-		 * Believe the time in the file system for lack of
-		 * anything better, resetting the RTC.
-		 */
-		s = splclock();
-		time.tv_sec = base;
-		time.tv_usec = 0;
-		splx(s);
-		printf("WARNING: preposterous clock chip time\n");
-		resettodr();
-		printf(" -- CHECK AND RESET THE DATE!\n");
-		return;
-	}
-
-	s = splclock();
-	time.tv_sec = rtc + rtc_offset * 60;
-	time.tv_usec = 0;
-	splx(s);
-
-	return;
-}
-
-void
-resettodr()
-{
-	struct clock_ymdhms dt;
-	int s;
-
-	if (!sh_clock.rtc_initialized)
-		return;
+	if (!(sh_clock.flags & SH_CLOCK_NORTC))
+		_reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START);
 
-	s = splclock();
-	clock_secs_to_ymdhms(time.tv_sec - rtc_offset * 60, &dt);
-	splx(s);
-
-	sh_clock.rtc.set(sh_clock.rtc._cookie, &dt);
-#ifdef DEBUG
-        printf("%s: %d/%d/%d/%d/%d/%d(%d) rtc_offset %d\n", __FUNCTION__,
-	    dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
-	    dt.dt_wday, rtc_offset);
-#endif
+	/*
+	 * Initialize timecounter.  We use TMU channel 2.
+	 */
+	sh_clock.tc.tc_get_timecount = sh_timecounter_get;
+	sh_clock.tc.tc_frequency = sh_clock.pclock / 4;
+	sh_clock.tc.tc_name = "tmu_pclock_4";
+	sh_clock.tc.tc_quality = 0;
+	sh_clock.tc.tc_counter_mask = 0xffffffff;
+	tc_init(&sh_clock.tc);
 }
 
-#endif /* __HAVE_GENERIC_TODR */
-
-
 #ifdef SH3
 int
 sh3_clock_intr(void *arg) /* trap frame */
@@ -417,17 +338,10 @@
  * SH3 RTC module ops.
  */
 
-void
-sh_rtc_init(void *cookie)
-{
-
-	/* Make sure to start RTC */
-	_reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START);
-}
-
-void
-sh_rtc_get(void *cookie, time_t base, struct clock_ymdhms *dt)
+int
+sh_rtc_gettime(todr_chip_handle_t tch, volatile struct timeval *tvp)
 {
+	struct clock_ymdhms dt;
 	int retry = 8;
 
 	/* disable carry interrupt */
@@ -440,12 +354,12 @@
 		_reg_write_1(SH_(RCR1), r);
 
 		if (CPU_IS_SH3)
-			dt->dt_year = FROMBCD(_reg_read_1(SH3_RYRCNT));
+			dt.dt_year = FROMBCD(_reg_read_1(SH3_RYRCNT));
 		else
-			dt->dt_year = FROMBCD(_reg_read_2(SH4_RYRCNT) & 0x00ff);
+			dt.dt_year = FROMBCD(_reg_read_2(SH4_RYRCNT) & 0x00ff);
 
 		/* read counter */
-#define	RTCGET(x, y)	dt->dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT)))
+#define	RTCGET(x, y)	dt.dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT)))
 		RTCGET(mon, MON);
 		RTCGET(wday, WK);
 		RTCGET(day, DAY);
@@ -457,20 +371,42 @@
 
 	if (retry == 0) {
 		printf("rtc_gettime: couldn't read RTC register.\n");
-		memset(dt, 0, sizeof(*dt));
-		return;
+		return -1;
+	}
+
+	dt.dt_year = (dt.dt_year % 100) + 1900;
+	if (dt.dt_year < 1970)
+		dt.dt_year += 100;
+
+	/*
+	 * If time_t is 32 bits, then the "End of Time" is 
+	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
+	 * This code copes with RTC's past the end of time if time_t
+	 * is an int32 or less. Needed because sometimes RTCs screw
+	 * up or are badly set, and that would cause the time to go
+	 * negative in the calculation below, which causes Very Bad
+	 * Mojo. This at least lets the user boot and fix the problem.
+	 * Note the code is self eliminating once time_t goes to 64 bits.
+	 */
+	if (sizeof(time_t) <= sizeof(int32_t)) {
+		if (dt.dt_year >= 2038) {
+			return -1;
+		}
 	}
 
-	dt->dt_year = (dt->dt_year % 100) + 1900;
-	if (dt->dt_year < 1970)
-		dt->dt_year += 100;
+	tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60;
+	tvp->tv_usec = 0;
+	return 0;
 }
 
-void
-sh_rtc_set(void *cookie, struct clock_ymdhms *dt)
+int
+sh_rtc_settime(todr_chip_handle_t tch, volatile struct timeval *tvp)
 {
+	struct clock_ymdhms dt;
 	uint8_t r;
 
+	clock_secs_to_ymdhms(tvp->tv_sec - rtc_offset * 60, &dt);
+
 	/* stop clock */
 	r = _reg_read_1(SH_(RCR2));
 	r |= SH_RCR2_RESET;
@@ -479,10 +415,10 @@
 
 	/* set time */
 	if (CPU_IS_SH3)
-		_reg_write_1(SH3_RYRCNT, TOBCD(dt->dt_year % 100));
+		_reg_write_1(SH3_RYRCNT, TOBCD(dt.dt_year % 100));
 	else
-		_reg_write_2(SH4_RYRCNT, TOBCD(dt->dt_year % 100));
-#define	RTCSET(x, y)	_reg_write_1(SH_(R ## x ## CNT), TOBCD(dt->dt_ ## y))
+		_reg_write_2(SH4_RYRCNT, TOBCD(dt.dt_year % 100));
+#define	RTCSET(x, y)	_reg_write_1(SH_(R ## x ## CNT), TOBCD(dt.dt_ ## y))
 	RTCSET(MON, mon);
 	RTCSET(WK, wday);
 	RTCSET(DAY, day);
@@ -492,4 +428,7 @@
 #undef RTCSET
 	/* start clock */
 	_reg_write_1(SH_(RCR2), r | SH_RCR2_START);
+
+	return 0;
 }
+