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); }
/* * 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); } }
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); }
/* * 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 */ }
/* * 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(µtime_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(µtime_slock); splx(s); *tvp = t; }