Timecounter support for acorn26, using IOC timer 0 and a bit of trickery.
This commit is contained in:
parent
aaaf91526d
commit
578a81db96
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue