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