#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/clockintr.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/stdint.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
static struct evcount cp0_clock_count;
static int cp0_clock_irq = 5;
uint64_t cp0_nsec_cycle_ratio;
uint64_t cp0_nsec_max;
int clockmatch(struct device *, void *, void *);
void clockattach(struct device *, struct device *, void *);
struct cfdriver clock_cd = {
NULL, "clock", DV_DULL
};
const struct cfattach clock_ca = {
sizeof(struct device), clockmatch, clockattach
};
void cp0_rearm_int5(void *, uint64_t);
void cp0_trigger_int5_wrapper(void *);
const struct intrclock cp0_intrclock = {
.ic_rearm = cp0_rearm_int5,
.ic_trigger = cp0_trigger_int5_wrapper
};
void cp0_initclock(void);
uint32_t cp0_int5(uint32_t, struct trapframe *);
void cp0_startclock(struct cpu_info *);
void cp0_trigger_int5(void);
void cp0_trigger_int5_masked(void);
int
clockmatch(struct device *parent, void *vcf, void *aux)
{
struct mainbus_attach_args *maa = aux;
return strcmp(maa->maa_name, clock_cd.cd_name) == 0;
}
void
clockattach(struct device *parent, struct device *self, void *aux)
{
uint64_t cp0_freq = curcpu()->ci_hw.clock / CP0_CYCLE_DIVIDER;
printf(": int 5\n");
cp0_nsec_cycle_ratio = cp0_freq * (1ULL << 32) / 1000000000;
cp0_nsec_max = UINT64_MAX / cp0_nsec_cycle_ratio;
set_intr(INTPRI_CLOCK, CR_INT_5, cp0_int5);
evcount_attach(&cp0_clock_count, "clock", &cp0_clock_irq);
evcount_percpu(&cp0_clock_count);
cp0_set_compare(cp0_get_count() - 1);
md_initclock = cp0_initclock;
md_startclock = cp0_startclock;
md_triggerclock = cp0_trigger_int5;
}
uint32_t
cp0_int5(uint32_t mask, struct trapframe *tf)
{
struct cpu_info *ci = curcpu();
int s;
evcount_inc(&cp0_clock_count);
cp0_set_compare(cp0_get_count() - 1);
if (!ci->ci_clock_started)
return CR_INT_5;
if (tf->ipl >= IPL_CLOCK) {
ci->ci_clock_deferred = 1;
return CR_INT_5;
}
ci->ci_clock_deferred = 0;
s = splclock();
#ifdef MULTIPROCESSOR
register_t sr;
sr = getsr();
ENABLEIPI();
#endif
clockintr_dispatch(tf);
#ifdef MULTIPROCESSOR
setsr(sr);
#endif
ci->ci_ipl = s;
return CR_INT_5;
}
void
cp0_rearm_int5(void *unused, uint64_t nsecs)
{
uint32_t cycles, t0;
register_t sr;
if (nsecs > cp0_nsec_max)
nsecs = cp0_nsec_max;
cycles = (nsecs * cp0_nsec_cycle_ratio) >> 32;
sr = disableintr();
t0 = cp0_get_count();
cp0_set_compare(t0 + cycles);
if (cycles <= cp0_get_count() - t0) {
if (!ISSET(cp0_get_cause(), CR_INT_5))
cp0_trigger_int5_masked();
}
setsr(sr);
}
void
cp0_trigger_int5(void)
{
register_t sr;
sr = disableintr();
cp0_trigger_int5_masked();
setsr(sr);
}
void
cp0_trigger_int5_masked(void)
{
uint32_t offset = 16, t0;
while (!ISSET(cp0_get_cause(), CR_INT_5)) {
t0 = cp0_get_count();
cp0_set_compare(t0 + offset);
if (cp0_get_count() - t0 < offset)
return;
offset *= 2;
}
}
void
cp0_trigger_int5_wrapper(void *unused)
{
cp0_trigger_int5();
}
void
cp0_initclock(void)
{
KASSERT(CPU_IS_PRIMARY(curcpu()));
stathz = hz;
profhz = stathz * 10;
statclock_is_randomized = 1;
}
void
cp0_startclock(struct cpu_info *ci)
{
int s;
if (!CPU_IS_PRIMARY(ci)) {
cp0_set_compare(cp0_get_count() - 1);
cp0_calibrate(ci);
}
clockintr_cpu_init(&cp0_intrclock);
s = splclock();
ci->ci_clock_started = 1;
clockintr_trigger();
splx(s);
}