Timecounter support for acorn26, using IOC timer 0 and a bit of trickery.

This commit is contained in:
bjh21 2006-10-04 23:40:00 +00:00
parent aaaf91526d
commit 578a81db96
3 changed files with 46 additions and 73 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: types.h,v 1.7 2006/09/03 11:51:29 bjh21 Exp $ */
/* $NetBSD: types.h,v 1.8 2006/10/04 23:40:00 bjh21 Exp $ */
#ifndef _ACORN26_TYPES_H_
#define _ACORN26_TYPES_H_
@ -8,5 +8,6 @@
#define __HAVE_GENERIC_SOFT_INTERRUPTS
#define __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
#define __HAVE_GENERIC_TODR
#define __HAVE_TIMECOUNTER
#endif /* _ACORN26_TYPES_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: ioc.c,v 1.14 2006/10/04 20:29:51 bjh21 Exp $ */
/* $NetBSD: ioc.c,v 1.15 2006/10/04 23:40:00 bjh21 Exp $ */
/*-
* Copyright (c) 1998, 1999, 2000 Ben Harris
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ioc.c,v 1.14 2006/10/04 20:29:51 bjh21 Exp $");
__KERNEL_RCSID(0, "$NetBSD: ioc.c,v 1.15 2006/10/04 23:40:00 bjh21 Exp $");
#include <sys/param.h>
#include <sys/device.h>
@ -39,6 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: ioc.c,v 1.14 2006/10/04 20:29:51 bjh21 Exp $");
#include <sys/queue.h>
#include <sys/reboot.h> /* For bootverbose */
#include <sys/systm.h>
#include <sys/timetc.h>
#include <machine/bus.h>
#include <machine/intr.h>
@ -58,6 +59,7 @@ static int ioc_search(struct device *parent, struct cfdata *cf,
static int ioc_print(void *aux, const char *pnp);
static int ioc_irq_clock(void *cookie);
static int ioc_irq_statclock(void *cookie);
static u_int ioc_get_timecount(struct timecounter *);
CFATTACH_DECL(ioc, sizeof(struct ioc_softc),
ioc_match, ioc_attach, NULL, NULL);
@ -333,6 +335,13 @@ cpu_initclocks(void)
sc->sc_dev.dv_xname, "clock");
sc->sc_clkirq = irq_establish(IOC_IRQ_TM0, IPL_CLOCK, ioc_irq_clock,
NULL, &sc->sc_clkev);
sc->sc_tc.tc_get_timecount = ioc_get_timecount;
sc->sc_tc.tc_counter_mask = ~(u_int)0;
sc->sc_tc.tc_frequency = IOC_TIMER_RATE;
sc->sc_tc.tc_name = sc->sc_dev.dv_xname;
sc->sc_tc.tc_quality = 100;
sc->sc_tc.tc_priv = sc;
tc_init(&sc->sc_tc);
if (bootverbose)
printf("%s: %d Hz clock interrupting at %s\n",
the_ioc->dv_xname, hz, irq_string(sc->sc_clkirq));
@ -365,7 +374,9 @@ cpu_initclocks(void)
static int
ioc_irq_clock(void *cookie)
{
struct ioc_softc *sc = (void *)the_ioc;
sc->sc_tcbase += t0_count + 1;
hardclock(cookie);
return IRQ_HANDLED;
}
@ -403,82 +414,40 @@ setstatclockrate(int hzrate)
KASSERT(hzrate == stathz);
}
void
microtime(struct timeval *tvp)
/*
* IOC timecounter
*
* We construct a timecounter from timer 0, which is also running the
* hardclock interrupt. Since the timer 0 resets on every hardclock
* interrupt, we keep track of the high-order bits of the counter in
* software, incrementing it on every hardclock. If hardclock
* interrupts are disabled, there's a period where the timer has reset
* but the interrupt handler hasn't incremented the hight-order bits.
* We detect this by checking whether there's a hardclock interrupt
* pending. We take a bit of extra care to ensure that we aren't
* confused by the interrupt happening between our latching the
* timer's count and reading the interrupt flag.
*/
static u_int
ioc_get_timecount(struct timecounter *tc)
{
static struct timeval lasttime;
struct timeval t;
struct device *self;
struct ioc_softc *sc;
bus_space_tag_t bst;
bus_space_handle_t bsh;
long sec, usec;
int t0, s, intbefore, intafter;
KASSERT(the_ioc != NULL);
self = the_ioc;
sc = (struct ioc_softc *)self;
bst = sc->sc_bst;
bsh = sc->sc_bsh;
struct ioc_softc *sc = tc->tc_priv;
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
u_int t0, count;
int s, intpending;
s = splclock();
t = time;
intbefore = ioc_irq_status(IOC_IRQ_TM0);
bus_space_write_1(bst, bsh, IOC_T0LATCH, 0);
if (__predict_false((intpending = ioc_irq_status(IOC_IRQ_TM0))))
bus_space_write_1(bst, bsh, IOC_T0LATCH, 0);
t0 = bus_space_read_1(bst, bsh, IOC_T0LOW);
t0 += bus_space_read_1(bst, bsh, IOC_T0HIGH) << 8;
intafter = ioc_irq_status(IOC_IRQ_TM0);
count = sc->sc_tcbase - t0;
if (intpending)
count += t0_count + 1;
splx(s);
/*
* If there's a timer interrupt pending, the counter has
* probably wrapped around once since "time" was last updated.
* Things are complicated by the fact that this could happen
* while we're trying to work out the time. We include some
* heuristics to spot this.
*
* NB: t0 counts down from t0_count to 0.
*/
if (intbefore || (intafter && t0 < t0_count / 2))
t0 -= t0_count;
t.tv_usec += (t0_count - t0) / (IOC_TIMER_RATE / 1000000);
while (t.tv_usec > 1000000) {
t.tv_usec -= 1000000;
t.tv_sec++;
}
/*
* Ordinarily, the current clock time is guaranteed to be later
* by at least one microsecond than the last time the clock was
* read. However, this rule applies only if the current time is
* within one second of the last time. Otherwise, the clock will
* (shudder) be set backward. The clock adjustment daemon or
* human equivalent is presumed to be correctly implemented and
* to set the clock backward only upon unavoidable crisis.
*/
sec = lasttime.tv_sec - t.tv_sec;
usec = lasttime.tv_usec - t.tv_usec;
if (usec < 0) {
usec += 1000000;
sec--;
}
if (sec == 0) {
t.tv_usec += usec + 1;
if (t.tv_usec >= 1000000) {
t.tv_usec -= 1000000;
t.tv_sec++;
}
}
lasttime = t;
*tvp = t;
return count;
}
void

View File

@ -1,4 +1,4 @@
/* $NetBSD: iocvar.h,v 1.3 2006/09/30 15:14:21 bjh21 Exp $ */
/* $NetBSD: iocvar.h,v 1.4 2006/10/04 23:40:00 bjh21 Exp $ */
/*-
* Copyright (c) 1998, 1999 Ben Harris
* All rights reserved.
@ -32,6 +32,7 @@
#ifndef _ARM26_IOCVAR_H
#define _ARM26_IOCVAR_H
#include <sys/timetc.h>
#include <machine/irq.h>
/* Structure passed to children of an IOC */
@ -58,6 +59,8 @@ struct ioc_softc {
struct evcnt sc_clkev;
struct irq_handler *sc_sclkirq;
struct evcnt sc_sclkev;
struct timecounter sc_tc;
u_int sc_tcbase;
u_int8_t sc_ctl;
};