#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/systimer.h>
#include <sys/sysctl.h>
#include <sys/signal.h>
#include <sys/interrupt.h>
#include <sys/bus.h>
#include <sys/time.h>
#include <machine/cpu.h>
#include <machine/clock.h>
#include <machine/globaldata.h>
#include <machine/md_var.h>
#include <machine/cothread.h>
#include <sys/thread2.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
int disable_rtc_set;
SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
CTLFLAG_RW, &disable_rtc_set, 0, "");
SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD,
&tsc_present, 0, "TSC Available");
SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD,
&tsc_invariant, 0, "Invariant TSC");
SYSCTL_INT(_hw, OID_AUTO, tsc_mpsync, CTLFLAG_RD,
&tsc_mpsync, 0, "TSC is synchronized across CPUs");
SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD,
&tsc_frequency, 0, "TSC Frequency");
int adjkerntz;
int wall_cmos_clock = 0;
SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock,
CTLFLAG_RD, &wall_cmos_clock, 0, "");
static cothread_t vktimer_cotd;
static int vktimer_running;
static sysclock_t vktimer_target;
static struct timespec vktimer_ts;
static sysclock_t vktimer_reload[MAXCPU];
extern int use_precise_timer;
static sysclock_t vkernel_timer_get_timecount(void);
static void vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock);
static void vktimer_thread(cothread_t cotd);
static struct cputimer vkernel_cputimer = {
.next = SLIST_ENTRY_INITIALIZER,
.name = "VKERNEL",
.pri = CPUTIMER_PRI_VKERNEL,
.type = CPUTIMER_VKERNEL,
.count = vkernel_timer_get_timecount,
.fromhz = cputimer_default_fromhz,
.fromus = cputimer_default_fromus,
.construct = vkernel_timer_construct,
.destruct = cputimer_default_destruct,
.freq = 1000000
};
static void vktimer_intr_reload(struct cputimer_intr *, sysclock_t);
static void vktimer_intr_initclock(struct cputimer_intr *, boolean_t);
static struct cputimer_intr vkernel_cputimer_intr = {
.freq = 1000000,
.reload = vktimer_intr_reload,
.enable = cputimer_intr_default_enable,
.config = cputimer_intr_default_config,
.restart = cputimer_intr_default_restart,
.pmfixup = cputimer_intr_default_pmfixup,
.initclock = vktimer_intr_initclock,
.pcpuhand = NULL,
.next = SLIST_ENTRY_INITIALIZER,
.name = "vkernel",
.type = CPUTIMER_INTR_VKERNEL,
.prio = CPUTIMER_INTR_PRIO_VKERNEL,
.caps = CPUTIMER_INTR_CAP_NONE,
.priv = NULL
};
static void
cpu_initclocks(void *arg __unused)
{
kprintf("initclocks\n");
cputimer_intr_register(&vkernel_cputimer_intr);
cputimer_intr_select(&vkernel_cputimer_intr, 0);
cputimer_register(&vkernel_cputimer);
cputimer_select(&vkernel_cputimer, 0);
}
SYSINIT(clocksvk, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL);
static void
vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock)
{
timer->base = 0;
timer->base = oclock - vkernel_timer_get_timecount();
}
static sysclock_t
vkernel_timer_get_timecount(void)
{
struct timespec ts;
sysclock_t count;
if (use_precise_timer)
clock_gettime(CLOCK_MONOTONIC_PRECISE, &ts);
else
clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
count = ts.tv_nsec / 1000;
count += (uint64_t)ts.tv_sec * 1000000;
return count;
}
static void
vktimer_intr_initclock(struct cputimer_intr *cti __unused,
boolean_t selected __unused)
{
vktimer_target = sys_cputimer->count();
vktimer_ts.tv_nsec = 1000000000 / 20;
vktimer_cotd = cothread_create(vktimer_thread, NULL, NULL, "vktimer");
while (vktimer_running == 0)
usleep(1000000 / 10);
#if 0
KKASSERT(kqueue_timer_info == NULL);
kqueue_timer_info = kqueue_add_timer(vktimer_intr, NULL);
#endif
}
static void
vktimer_sigint(int signo)
{
}
static sysclock_t
vktimer_gettick_us(void)
{
struct clockinfo info;
int mib[] = { CTL_KERN, KERN_CLOCKRATE };
size_t len = sizeof(info);
if (sysctl(mib, NELEM(mib), &info, &len, NULL, 0) != 0 ||
len != sizeof(info)) {
return 1000000 / 100;
} else if (info.tick < 999999) {
return info.tick;
} else {
return 1000000 / 100;
}
}
static void
vktimer_thread(cothread_t cotd)
{
struct sigaction sa;
globaldata_t gscan;
sysclock_t ticklength_us;
bzero(&sa, sizeof(sa));
sa.sa_handler = vktimer_sigint;
sa.sa_flags |= SA_NODEFER;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
ticklength_us = vktimer_gettick_us();
vktimer_running = 1;
while (vktimer_cotd == NULL)
usleep(1000000 / 10);
for (;;) {
sysclock_t curtime;
sysclock_t reload;
ssysclock_t delta;
sysclock_t freq;
int n;
cothread_sleep(cotd, &vktimer_ts);
rescan:
freq = sys_cputimer->freq;
curtime = sys_cputimer->count();
reload = freq - 1;
for (n = 0; n < ncpus; ++n) {
gscan = globaldata_find(n);
delta = vktimer_reload[n] - curtime;
if (delta <= 0 && TAILQ_FIRST(&gscan->gd_systimerq))
pthread_kill(ap_tids[n], SIGURG);
if (delta > 0 && reload > delta)
reload = delta;
}
reload += curtime;
vktimer_target = reload;
reload -= curtime;
for (n = 0; n < ncpus; ++n) {
gscan = globaldata_find(n);
delta = vktimer_reload[n] - curtime;
if (delta > 0 && reload > delta)
goto rescan;
}
if (sys_cputimer == &vkernel_cputimer &&
!use_precise_timer && reload < ticklength_us / 10) {
reload = ticklength_us / 10;
}
vktimer_ts.tv_nsec = muldivu64(reload, 1000000000, freq);
}
}
static void
vktimer_intr_reload(struct cputimer_intr *cti __unused, sysclock_t reload)
{
if ((ssysclock_t)reload < 0)
reload = 1;
if (reload >= sys_cputimer->freq)
reload = sys_cputimer->freq;
reload += sys_cputimer->count();
vktimer_reload[mycpu->gd_cpuid] = reload;
if (vktimer_cotd && (ssysclock_t)(reload - vktimer_target) < 0) {
while ((sysclock_t)(reload - vktimer_target) < 0)
reload = atomic_swap_long(&vktimer_target, reload);
cothread_wakeup(vktimer_cotd, &vktimer_ts);
}
}
void
vktimer_intr(struct intrframe *frame)
{
struct globaldata *gd = mycpu;
sysclock_t sysclock_count;
sysclock_count = sys_cputimer->count();
++gd->gd_cnt.v_timer;
systimer_intr(&sysclock_count, 0, frame);
}
void
inittodr(time_t base)
{
struct timespec ts;
struct timeval tv;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
set_timeofday(&ts);
}
void
resettodr(void)
{
}
void
DELAY(int usec)
{
crit_enter();
usleep(usec);
crit_exit();
}
void
DRIVERSLEEP(int usec)
{
if (mycpu->gd_intr_nesting_level)
DELAY(usec);
else if (1000000 / usec >= hz)
tsleep(DRIVERSLEEP, 0, "DELAY", 1000000 / usec / hz + 1);
else
DELAY(usec);
}