static void
mcclock_set_pcc_freq(struct mc146818_softc *sc)
{
	struct cpu_info *ci;
	uint64_t freq;
	uint32_t ctrdiff[NLOOP], pcc_start, pcc_end;
	uint8_t reg_a;
	int i;

	/* save REG_A */
	reg_a = (*sc->sc_mcread)(sc, MC_REGA);

	/* set interval 16Hz to measure pcc */
	(*sc->sc_mcwrite)(sc, MC_REGA, MC_BASE_32_KHz | MC_RATE_16_Hz);

	/* clear interrupt flags */
	(void)(*sc->sc_mcread)(sc, MC_REGC);

	/* Run the loop an extra time to prime the cache. */
	for (i = 0; i < NLOOP; i++) {

		/* wait till the periodic interrupt flag is set */
		while (((*sc->sc_mcread)(sc, MC_REGC) & MC_REGC_PF) == 0)
			;
		pcc_start = cpu_counter32();

		/* wait till the periodic interrupt flag is set again */
		while (((*sc->sc_mcread)(sc, MC_REGC) & MC_REGC_PF) == 0)
			;
		pcc_end = cpu_counter32();

		ctrdiff[i] = pcc_end - pcc_start;
	}

	freq = ((ctrdiff[NLOOP - 2] + ctrdiff[NLOOP - 1]) * 16 /* Hz */) / 2;

	/* restore REG_A */
	(*sc->sc_mcwrite)(sc, MC_REGA, reg_a);

	/* XXX assume all processors have the same clock and frequency */
	for (ci = &cpu_info_primary; ci; ci = ci->ci_next)
		ci->ci_pcc_freq = freq;
}
/*
 * Generate a 32-bit counter.  This should be more machine dependent,
 * using cycle counters and the like when possible.
 */
static inline u_int32_t
rndpseudo_counter(void)
{
	struct timeval tv;

#if defined(__HAVE_CPU_COUNTER) && !defined(_RUMPKERNEL) /* XXX: bad pooka */
	if (cpu_hascounter())
		return (cpu_counter32());
#endif
	microtime(&tv);
	return (tv.tv_sec * 1000000 + tv.tv_usec);
}
Beispiel #3
0
/*
 * This routine is called about once per second directly by the master
 * processor and via an interprocessor interrupt for other processors.
 * It determines the CC frequency of each processor relative to the
 * master clock and the time this determination is made.  These values
 * are used by microtime() to interpolate the microseconds between
 * timer interrupts.  Note that we assume the kernel variables have
 * been zeroed early in life.
 */
void
cc_microset(struct cpu_info *ci)
{
	struct timeval t;
	int64_t delta, denom;

	/* Note: Clock interrupts are already blocked. */

	denom = ci->ci_cc_cc;
	t = cc_microset_time;		/* XXXSMP: not atomic */
	ci->ci_cc_cc = cpu_counter32();

	if (ci->ci_cc_denom == 0) {
		/*
		 * This is our first time here on this CPU.  Just
		 * start with reasonable initial values.
		 */
		ci->ci_cc_time = t;
		ci->ci_cc_ms_delta = 1000000;
		ci->ci_cc_denom = cpu_frequency(ci);
		return;
	}

	denom = ci->ci_cc_cc - denom;
	if (denom < 0)
		denom += 0x100000000L;

	delta = (t.tv_sec - ci->ci_cc_time.tv_sec) * 1000000 +
	    (t.tv_usec - ci->ci_cc_time.tv_usec);

	ci->ci_cc_time = t;
	/*
	 * Make sure it's within .5 to 1.5 seconds -- otherwise,
	 * the time is probably be frobbed with by the timekeeper
	 * or the human.
	 */
	if (delta > 500000 && delta < 1500000) {
		ci->ci_cc_ms_delta = delta;
		ci->ci_cc_denom = denom;
#if 0
		printf("cc_microset: delta %" PRId64 ", denom %" PRId64 "\n",
		       delta, denom);
#endif
	} else {
#if 0
		printf("cc_microset: delta %" PRId64 ", resetting state\n",
		       delta);
#endif
		ci->ci_cc_ms_delta = 1000000;
		ci->ci_cc_denom = cpu_frequency(ci);
	}
}
Beispiel #4
0
static inline uint32_t
cprng_counter(void)
{
	struct timeval tv;

#if defined(__HAVE_CPU_COUNTER)
	if (cpu_hascounter())
		return cpu_counter32();
#endif
	if (__predict_false(cold)) {
		static int ctr;
		/* microtime unsafe if clock not running yet */
		return ctr++;
	}
	getmicrotime(&tv);
	return (tv.tv_sec * 1000000 + tv.tv_usec);
}
Beispiel #5
0
/*
 * This routine is called about once per second directly by the master
 * processor and via an interprocessor interrupt for other processors.
 * It determines the CC frequency of each processor relative to the
 * master clock and the time this determination is made.  These values
 * are used by cc_get_timecount() to interpolate the ticks between
 * timer interrupts.  Note that we assume the kernel variables have
 * been zeroed early in life.
 */
void
cc_calibrate_cpu(struct cpu_info *ci)
{
	u_int   gen;
	int64_t val;
	int64_t delta, denom;
	int s;
#ifdef TIMECOUNTER_DEBUG
	int64_t factor, old_factor;
#endif
	val = cc_cal_val;

	s = splhigh();
	/* create next generation number */
	gen = ci->ci_cc.cc_gen;
	gen++;
	if (gen == 0)
		gen++;

	/* update in progress */
	ci->ci_cc.cc_gen = 0;

	denom = ci->ci_cc.cc_cc;
	ci->ci_cc.cc_cc = cpu_counter32();

	if (ci->ci_cc.cc_denom == 0) {
		/*
		 * This is our first time here on this CPU.  Just
		 * start with reasonable initial values.
		 */
		ci->ci_cc.cc_val = val;
		ci->ci_cc.cc_denom = cpu_frequency(ci);
		if (ci->ci_cc.cc_denom == 0)
			ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;
		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
		ci->ci_cc.cc_gen = gen;
		splx(s);
		return;
	}

#ifdef TIMECOUNTER_DEBUG
	old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
#endif

	/* local ticks per period */
	denom = ci->ci_cc.cc_cc - denom;
	if (denom < 0)
		denom += 0x100000000LL;

	ci->ci_cc.cc_denom = denom;

	/* reference ticks per period */
	delta = val - ci->ci_cc.cc_val;
	if (delta < 0)
		delta += 0x100000000LL;

	ci->ci_cc.cc_val = val;
	ci->ci_cc.cc_delta = delta;
	
	/* publish new generation number */
	ci->ci_cc.cc_gen = gen;
	splx(s);

#ifdef TIMECOUNTER_DEBUG
	factor = (delta * 1000) / denom - old_factor;
	if (factor < 0)
		factor = -factor;

	if (factor > old_factor / 10)
		printf("cc_calibrate_cpu[%u]: 10%% exceeded - delta %"
		    PRId64 ", denom %" PRId64 ", factor %" PRId64
		    ", old factor %" PRId64"\n", ci->ci_index,
		    delta, denom, (delta * 1000) / denom, old_factor);
#endif /* TIMECOUNTER_DEBUG */
}
Beispiel #6
0
/*
 * Return the best possible estimate of the time in the timeval to which
 * tvp points.  The kernel logical time variable is interpolated between
 * ticks by reading the CC to determine the number of cycles since the
 * last processor update, then converting the result to microseconds.  In
 * the case of multiprocessor systems, the interpolation is specific to
 * each processor, since each processor has its own CC.
 */
void
cc_microtime(struct timeval *tvp)
{
	static struct timeval lasttime;
	static struct simplelock microtime_slock = SIMPLELOCK_INITIALIZER;
	struct timeval t, st;
	struct cpu_info *ci = curcpu();
	int64_t sec, usec;
	int s;

#ifdef MULTIPROCESSOR
	s = splipi();		/* also blocks IPIs */
#else
	s = splclock();		/* block clock interrupts */
#endif

	/* XXXSMP: not atomic */
	st = time;		/* read system time */

	if (ci->ci_cc_denom != 0) {
		/*
		 * Determine the current clock time as the time at last
		 * microset() call, plus the CC accumulation since then.
		 * This time should lie in the interval between the current
		 * master clock time and the time at the nexdt tick, but
		 * this code does not explicitly require that in the interest
		 * of speed.  If something ugly occurs, like a settimeofday()
		 * call, the processors may disagree among themselves for not
		 * more than the interval between microset() calls.  In any
		 * case, the following sanity checks will suppress timewarps.
		 */
		t = ci->ci_cc_time;
		usec = cpu_counter32() - ci->ci_cc_cc;
		if (usec < 0)
			usec += 0x100000000L;
		t.tv_usec += (usec * ci->ci_cc_ms_delta) / ci->ci_cc_denom;
		while (t.tv_usec >= 1000000) {
			t.tv_usec -= 1000000;
			t.tv_sec++;
		}
	} else {
		/*
		 * Can't use the CC -- just use the system time.
		 */
		t = st;
	}

	/*
	 * 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.
	 */
	simple_lock(&microtime_slock);
	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;
	simple_unlock(&microtime_slock);

	splx(s);

	*tvp = t;
}