Subject: Re: sh3 todr and timecounter conversion (rev2)
To: None <port-sh3@NetBSD.org>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: port-sh3
Date: 09/22/2006 22:04:59
I wrote:

> > 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)

I'd also like to commit this Garrett's timecounter changes soon.
It just works fine on my dreamcast with several fixes:

- explicitly start TMU2 for timecounter(9) in cpu_initclocks(9)
- clear "softpend" (that indicates softintr type) if tmu1_intr() ack'ed
- use correct flags to check softintr types in tmu1_intr()

The attached diff also contains evcnt(9) stuff for hardclock(9)
and some misc cosmetics.
---
Izumi Tsutsui


Index: sys/arch/sh3/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/include/types.h,v
retrieving revision 1.23
diff -u -r1.23 types.h
--- sys/arch/sh3/include/types.h	20 Sep 2006 00:41:12 -0000	1.23
+++ sys/arch/sh3/include/types.h	22 Sep 2006 12:57:34 -0000
@@ -68,6 +68,7 @@
 #define	__HAVE_GENERIC_SOFT_INTERRUPTS
 #define	__HAVE_GENERIC_TODR
 #define	__HAVE_SYSCALL_INTERN
+#define	__HAVE_TIMECOUNTER
 
 #if defined(_KERNEL)
 #define	__HAVE_RAS
Index: sys/arch/sh3/sh3/clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/clock.c,v
retrieving revision 1.33
diff -u -r1.33 clock.c
--- sys/arch/sh3/sh3/clock.c	20 Sep 2006 00:41:12 -0000	1.33
+++ sys/arch/sh3/sh3/clock.c	22 Sep 2006 12:57:34 -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>
 
@@ -68,9 +69,10 @@
  * NetBSD/sh3 clock module
  *  + default 64Hz
  *  + use TMU channel 0 as clock interrupt source.
- *  + use TMU channel 1 and 2 as emulated software interrupt soruce.
+ *  + use TMU channel 1 as emulated software interrupt soruce.
+ *  + use TMU channel 2 as freerunning counter for timecounter.
  *  + If RTC module is active, TMU channel 0 input source is RTC output.
- *    (1.6384kHz)
+ *    (16.384kHz)
  */
 struct {
 	/* Hard clock */
@@ -81,6 +83,8 @@
 	uint32_t pclock;	/* PCLOCK */
 	uint32_t cpuclock;	/* CPU clock */
 	int flags;
+
+	struct timecounter tc;
 } sh_clock = {
 #ifdef PCLOCK
 	.pclock = PCLOCK,
@@ -89,10 +93,14 @@
 
 uint32_t maxwdog;
 
+struct evcnt sh_hardclock_evcnt =
+    EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "tmu0", "hardclock");
+
 /* TMU */
 /* interrupt handler is timing critical. prepared for each. */
 int sh3_clock_intr(void *);
 int sh4_clock_intr(void *);
+u_int sh_timecounter_get(struct timecounter *);
 
 /*
  * Estimate CPU and Peripheral clock.
@@ -198,34 +206,11 @@
 	/* XXX not yet */
 }
 
-/*
- * Return the best possible estimate of the time in the timeval to
- * which tv points.
- */
-void
-microtime(struct timeval *tv)
+u_int
+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));
 }
 
 /*
@@ -270,18 +255,35 @@
 	_reg_write_4(SH_(TCOR0), sh_clock.hz_cnt);
 	_reg_write_4(SH_(TCNT0), sh_clock.hz_cnt);
 
+	evcnt_attach_static(&sh_hardclock_evcnt);
 	intc_intr_establish(SH_INTEVT_TMU0_TUNI0, IST_LEVEL, IPL_CLOCK,
 	    CPU_IS_SH3 ? sh3_clock_intr : sh4_clock_intr, 0);
 	/* start hardclock */
 	_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 freerunning counter for timecounter(9).
+	 */
+	_reg_write_2(SH_(TCR2), TCR_TPSC_P4);
 	_reg_write_4(SH_(TCOR2), 0xffffffff);
+
+	/*
+	 * Start and initialize timecounter.
+	 */
+	_reg_bset_1(SH_(TSTR), TSTR_STR2);
+
+	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);
 }
 
 
@@ -297,6 +299,9 @@
 		maxwdog = i;
 	wdog_wr_cnt(0);			/* reset to zero */
 #endif
+
+	sh_hardclock_evcnt.ev_count++;
+
 	/* clear underflow status */
 	_reg_bclr_2(SH3_TCR0, TCR_UNF);
 
@@ -317,6 +322,9 @@
 		maxwdog = i;
 	wdog_wr_cnt(0);			/* reset to zero */
 #endif
+
+	sh_hardclock_evcnt.ev_count++;
+
 	/* clear underflow status */
 	_reg_bclr_2(SH4_TCR0, TCR_UNF);
 
Index: sys/arch/sh3/sh3/interrupt.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/interrupt.c,v
retrieving revision 1.18
diff -u -r1.18 interrupt.c
--- sys/arch/sh3/sh3/interrupt.c	25 Jan 2006 00:02:57 -0000	1.18
+++ sys/arch/sh3/sh3/interrupt.c	22 Sep 2006 12:57:34 -0000
@@ -63,10 +63,7 @@
 #endif
 
 static void netintr(void);
-static void tmu1_oneshot(void);
 static int tmu1_intr(void *);
-static void tmu2_oneshot(void);
-static int tmu2_intr(void *);
 
 /*
  * EVTCODE to intc_intrhand mapper.
@@ -597,10 +594,13 @@
 	    (void (*)(void *))netintr, NULL);
 	KDASSERT(softnet_intrhand != NULL);
 
+	/*
+	 * This runs at the lowest soft priority, so that when splx() sets
+	 * a higher priority it blocks all soft interrupts.  Effectively, we
+	 * have only a single soft interrupt level this way.
+	 */
 	intc_intr_establish(SH_INTEVT_TMU1_TUNI1, IST_LEVEL, IPL_SOFT,
 	    tmu1_intr, NULL);
-	intc_intr_establish(SH_INTEVT_TMU2_TUNI2, IST_LEVEL, IPL_SOFTNET,
-	    tmu2_intr, NULL);
 }
 
 void
@@ -631,16 +631,6 @@
 	_cpu_intr_resume(s);
 }
 
-void
-setsoft(int ipl)
-{
-
-	if (ipl < IPL_SOFTNET)
-		tmu1_oneshot();
-	else
-		tmu2_oneshot();
-}
-
 /* Register a software interrupt handler. */
 void *
 softintr_establish(int ipl, void (*func)(void *), void *arg)
@@ -712,46 +702,43 @@
 /*
  * Software interrupt is simulated with TMU one-shot timer.
  */
-static void
-tmu1_oneshot(void)
-{
+static volatile u_int softpend;
 
+void
+setsoft(int ipl)
+{
+	int s;
+	s = splsoftserial();
+	softpend |= (1 << ipl);
 	_reg_bclr_1(SH_(TSTR), TSTR_STR1);
 	_reg_write_4(SH_(TCNT1), 0);
 	_reg_bset_1(SH_(TSTR), TSTR_STR1);
+	splx(s);
 }
 
 static int
 tmu1_intr(void *arg)
 {
+	u_int pend;
+	int s;
+
+	s = splhigh();
+	pend = softpend;
+	softpend = 0;
+	splx(s);
 
 	_reg_bclr_1(SH_(TSTR), TSTR_STR1);
 	_reg_bclr_2(SH_(TCR1), TCR_UNF);
 
-	softintr_dispatch(IPL_SOFTCLOCK);
-	softintr_dispatch(IPL_SOFT);
-
+	if (pend & (1 << IPL_SOFTSERIAL))
+		softintr_dispatch(IPL_SOFTSERIAL);
+	if (pend & (1 << IPL_SOFTNET))
+		softintr_dispatch(IPL_SOFTNET);
+	if (pend & (1 << IPL_SOFTCLOCK))
+		softintr_dispatch(IPL_SOFTCLOCK);
+	if (pend & (1 << IPL_SOFT))
+		softintr_dispatch(IPL_SOFT);
+		
 	return (0);
 }
 
-static void
-tmu2_oneshot(void)
-{
-
-	_reg_bclr_1(SH_(TSTR), TSTR_STR2);
-	_reg_write_4(SH_(TCNT2), 0);
-	_reg_bset_1(SH_(TSTR), TSTR_STR2);
-}
-
-static int
-tmu2_intr(void *arg)
-{
-
-	_reg_bclr_1(SH_(TSTR), TSTR_STR2);
-	_reg_bclr_2(SH_(TCR2), TCR_UNF);
-
-	softintr_dispatch(IPL_SOFTSERIAL);
-	softintr_dispatch(IPL_SOFTNET);
-
-	return (0);
-}