#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.124 2024/07/12 22:31:40 andvar Exp $");
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#ifdef GPROF
#include <sys/gmon.h>
#endif
#include <uvm/uvm_extern.h>
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <machine/eeprom.h>
#include <machine/cpu.h>
#include <sparc64/sparc64/intreg.h>
#include <sparc64/sparc64/timerreg.h>
#include <sparc64/dev/iommureg.h>
#include "psycho.h"
#if NPSYCHO > 0
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/iommuvar.h>
#include <sparc64/dev/psychoreg.h>
#include <sparc64/dev/psychovar.h>
#endif
int statvar = 8192;
int statmin;
int timerok;
#ifndef MULTIPROCESSOR
static int statscheddiv;
static struct intrhand *schedint;
#endif
static int timermatch(device_t, cfdata_t, void *);
static void timerattach(device_t, device_t, void *);
struct timerreg_4u timerreg_4u;
CFATTACH_DECL_NEW(timer, 0,
timermatch, timerattach, NULL, NULL);
struct chiptime;
void stopcounter(struct timer_4u *);
int timerblurb = 10;
static u_int tick_get_timecount(struct timecounter *);
static u_int stick_get_timecount(struct timecounter *);
#if NPSYCHO > 0
static u_int stick2e_get_timecount(struct timecounter *);
#endif
static struct timecounter tick_timecounter = {
.tc_get_timecount = tick_get_timecount,
.tc_counter_mask = ~0u,
.tc_name = "tick-counter",
.tc_quality = 100,
};
static struct timecounter stick_timecounter = {
.tc_get_timecount = stick_get_timecount,
.tc_counter_mask = ~0u,
.tc_name = "stick-counter",
.tc_quality = 200,
};
#if NPSYCHO > 0
static struct timecounter stick2e_timecounter = {
.tc_get_timecount = stick2e_get_timecount,
.tc_counter_mask = ~0u,
.tc_name = "stick-counter",
.tc_quality = 200,
};
#endif
static u_int
tick_get_timecount(struct timecounter *tc)
{
return gettick();
}
static u_int
stick_get_timecount(struct timecounter *tc)
{
return getstick();
}
#if NPSYCHO > 0
static u_int
stick2e_get_timecount(struct timecounter *tc)
{
return psycho_getstick32();
}
#endif
#ifdef MULTIPROCESSOR
static u_int counter_get_timecount(struct timecounter *);
static struct timecounter counter_timecounter = {
counter_get_timecount,
0,
TMR_LIM_MASK,
1000000,
"counter-timer",
200,
0,
NULL
};
static u_int
counter_get_timecount(struct timecounter *tc)
{
return (u_int)ldxa((vaddr_t)&timerreg_4u.t_timer[1].t_count,
ASI_NUCLEUS) & TMR_LIM_MASK;
}
#endif
static int
timermatch(device_t parent, cfdata_t cf, void *aux)
{
struct mainbus_attach_args *ma = aux;
return (strcmp("counter-timer", ma->ma_name) == 0);
}
static void
timerattach(device_t parent, device_t self, void *aux)
{
struct mainbus_attach_args *ma = aux;
u_int *va = ma->ma_address;
#if 0
volatile int64_t *cnt = NULL, *lim = NULL;
#endif
timerreg_4u.t_timer = (struct timer_4u *)(u_long)va[0];
timerreg_4u.t_clrintr = (int64_t *)(u_long)va[1];
timerreg_4u.t_mapintr = (int64_t *)(u_long)va[2];
stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS,
(timerreg_4u.t_mapintr[0] & ~(INTMAP_V|INTMAP_TID)) |
(CPU_UPAID << INTMAP_TID_SHIFT));
stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS,
(timerreg_4u.t_mapintr[1] & ~(INTMAP_V|INTMAP_TID)) |
(CPU_UPAID << INTMAP_TID_SHIFT));
struct intrhand *level10 = intrhand_alloc();
level10->ih_fun = clockintr;
level10->ih_pil = PIL_CLOCK;
level10->ih_number = INTVEC(ma->ma_interrupts[0]);
level10->ih_clr = &timerreg_4u.t_clrintr[0];
intr_establish(PIL_CLOCK, true, level10);
printf(" irq vectors %lx", (u_long)level10->ih_number);
#ifndef MULTIPROCESSOR
struct intrhand *level14 = intrhand_alloc();
level14->ih_fun = statintr;
level14->ih_pil = PIL_STATCLOCK;
level14->ih_number = INTVEC(ma->ma_interrupts[1]);
level14->ih_clr = &timerreg_4u.t_clrintr[1];
intr_establish(PIL_STATCLOCK, true, level14);
printf(" and %lx", (u_long)level14->ih_number);
#endif
#if 0
cnt = &(timerreg_4u.t_timer[0].t_count);
lim = &(timerreg_4u.t_timer[0].t_limit);
#ifdef DEBUG
printf("Delay calibration....\n");
#endif
for (timerblurb = 1; timerblurb > 0; timerblurb++) {
volatile int discard;
register int t0, t1;
discard = *lim;
*lim = tmr_ustolim(TMR_MASK-1);
t0 = *cnt;
delay(100);
t1 = *cnt;
if (t1 & TMR_LIMIT)
panic("delay calibration");
t0 = (t0 >> TMR_SHIFT) & TMR_MASK;
t1 = (t1 >> TMR_SHIFT) & TMR_MASK;
if (t1 >= t0 + 100)
break;
}
printf(" delay constant %d\n", timerblurb);
#endif
printf("\n");
timerok = 1;
}
void
stopcounter(struct timer_4u *creg)
{
volatile struct timer_4u *reg = creg;
(void)reg->t_limit;
reg->t_limit = 0;
}
void
tickintr_establish(int pil, int (*fun)(void *))
{
int s;
struct intrhand *ih;
struct cpu_info *ci = curcpu();
ih = sparc_softintr_establish(pil, fun, NULL);
ih->ih_number = 1;
if (CPU_IS_PRIMARY(ci))
intr_establish(pil, true, ih);
ci->ci_tick_ih = ih;
ci->ci_tick_increment = ci->ci_cpu_clockrate[0] / hz;
s = intr_disable();
next_tick(ci->ci_tick_increment);
intr_restore(s);
}
void
stickintr_establish(int pil, int (*fun)(void *))
{
int s;
struct intrhand *ih;
struct cpu_info *ci = curcpu();
ih = sparc_softintr_establish(pil, fun, NULL);
ih->ih_number = 1;
if (CPU_IS_PRIMARY(ci))
intr_establish(pil, true, ih);
ci->ci_tick_ih = ih;
ci->ci_tick_increment = ci->ci_system_clockrate[0] / hz;
s = intr_disable();
next_stick_init();
next_stick(ci->ci_tick_increment);
intr_restore(s);
}
#if NPSYCHO > 0
void
stick2eintr_establish(int pil, int (*fun)(void *))
{
int s;
struct intrhand *ih;
struct cpu_info *ci = curcpu();
ih = sparc_softintr_establish(pil, fun, NULL);
ih->ih_number = 1;
if (CPU_IS_PRIMARY(ci))
intr_establish(pil, true, ih);
ci->ci_tick_ih = ih;
ci->ci_tick_increment = ci->ci_system_clockrate[0] / hz;
s = intr_disable();
psycho_nextstick(ci->ci_tick_increment);
intr_restore(s);
}
#endif
void
cpu_initclocks(void)
{
struct cpu_info *ci = curcpu();
#ifndef MULTIPROCESSOR
int statint, minint;
#endif
#ifdef DEBUG
extern int intrdebug;
#endif
#ifdef DEBUG
if (intrdebug) {
hz = 1;
tick = 1000000 / hz;
printf("intrdebug set: 1Hz clock\n");
}
#endif
if (1000000 % hz) {
printf("cannot get %d Hz clock; using 100 Hz\n", hz);
hz = 100;
tick = 1000000 / hz;
}
if (!ci->ci_cpu_clockrate[0]) {
ci->ci_cpu_clockrate[0] = 200000000;
ci->ci_cpu_clockrate[1] = 200000000 / 1000000;
}
if (CPU_ISSUN4U || CPU_ISSUN4US)
settick(0);
tick_timecounter.tc_frequency = ci->ci_cpu_clockrate[0];
tc_init(&tick_timecounter);
if (ci->ci_system_clockrate[0] != 0) {
if (CPU_ISSUN4U && CPU_IS_HUMMINGBIRD()) {
#if NPSYCHO > 0
psycho_setstick(0);
stick2e_timecounter.tc_frequency =
ci->ci_system_clockrate[0];
tc_init(&stick2e_timecounter);
#endif
} else {
if (CPU_ISSUN4U || CPU_ISSUN4US)
setstick(0);
stick_timecounter.tc_frequency =
ci->ci_system_clockrate[0];
tc_init(&stick_timecounter);
}
}
if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) {
if (ci->ci_system_clockrate[0] == 0) {
aprint_normal("No counter-timer -- using %%tick "
"at %sMHz as system clock.\n",
clockfreq(ci->ci_cpu_clockrate[0]));
tickintr_establish(PIL_CLOCK, tickintr);
} else if (CPU_ISSUN4U && CPU_IS_HUMMINGBIRD()) {
#if NPSYCHO > 0
aprint_normal("No counter-timer -- using STICK "
"at %sMHz as system clock.\n",
clockfreq(ci->ci_system_clockrate[0]));
stick2eintr_establish(PIL_CLOCK, stick2eintr);
#else
panic("trying to use STICK without psycho?!");
#endif
} else {
aprint_normal("No counter-timer -- using %%stick "
"at %sMHz as system clock.\n",
clockfreq(ci->ci_system_clockrate[0]));
stickintr_establish(PIL_CLOCK, stickintr);
}
stathz = 0;
return;
}
#ifndef MULTIPROCESSOR
if (stathz == 0)
stathz = hz;
if (1000000 % stathz) {
printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
stathz = 100;
}
profhz = stathz;
statint = 1000000 / stathz;
minint = statint / 2 + 100;
while (statvar > minint)
statvar >>= 1;
schedint = sparc_softintr_establish(PIL_SCHED, schedintr, NULL);
if (stathz > 60)
schedhz = 16;
else
schedhz = stathz / 2 + 1;
statscheddiv = stathz / schedhz;
if (statscheddiv <= 0)
panic("statscheddiv");
#endif
stxa((vaddr_t)&timerreg_4u.t_timer[0].t_limit, ASI_NUCLEUS,
tmr_ustolim(tick)|TMR_LIM_IEN|TMR_LIM_PERIODIC|TMR_LIM_RELOAD);
stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS,
timerreg_4u.t_mapintr[0]|INTMAP_V);
#ifdef MULTIPROCESSOR
stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
TMR_LIM_MASK);
tc_init(&counter_timecounter);
#else
#ifdef DEBUG
if (intrdebug)
stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
tmr_ustolim(statint)|TMR_LIM_RELOAD);
else
#endif
stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
tmr_ustolim(statint)|TMR_LIM_IEN|TMR_LIM_RELOAD);
stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS,
timerreg_4u.t_mapintr[1]|INTMAP_V|(CPU_UPAID << INTMAP_TID_SHIFT));
statmin = statint - (statvar >> 1);
#endif
}
void
setstatclockrate(int newhz)
{
}
#ifdef DEBUG
static int clockcheck = 0;
#endif
int
clockintr(void *cap)
{
#ifdef DEBUG
static int64_t tick_base = 0;
struct timeval ctime;
int64_t t = gettick();
microtime(&ctime);
if (!tick_base) {
tick_base = (ctime.tv_sec * 1000000LL + ctime.tv_usec)
/ curcpu()->ci_cpu_clockrate[1];
tick_base -= t;
} else if (clockcheck) {
int64_t tk = t;
int64_t clk = (ctime.tv_sec * 1000000LL + ctime.tv_usec);
t -= tick_base;
t = t / curcpu()->ci_cpu_clockrate[1];
if (t - clk > hz) {
printf("Clock lost an interrupt!\n");
printf("Actual: %llx Expected: %llx tick %llx tick_base %llx\n",
(long long)t, (long long)clk, (long long)tk, (long long)tick_base);
tick_base = 0;
}
}
#endif
hardclock((struct clockframe *)cap);
return (1);
}
int
tickintr(void *cap)
{
int s;
hardclock((struct clockframe *)cap);
s = intr_disable();
next_tick(curcpu()->ci_tick_increment);
intr_restore(s);
curcpu()->ci_tick_evcnt.ev_count++;
return (1);
}
int
stickintr(void *cap)
{
int s;
hardclock((struct clockframe *)cap);
s = intr_disable();
next_stick(curcpu()->ci_tick_increment);
intr_restore(s);
curcpu()->ci_tick_evcnt.ev_count++;
return (1);
}
#if NPSYCHO > 0
int
stick2eintr(void *cap)
{
int s;
hardclock((struct clockframe *)cap);
s = intr_disable();
psycho_nextstick(curcpu()->ci_tick_increment);
intr_restore(s);
curcpu()->ci_tick_evcnt.ev_count++;
return (1);
}
#endif
#ifndef MULTIPROCESSOR
int
statintr(void *cap)
{
register u_long newint, r, var;
struct cpu_info *ci = curcpu();
#ifdef NOT_DEBUG
printf("statclock: count %x:%x, limit %x:%x\n",
timerreg_4u.t_timer[0].t_count, timerreg_4u.t_timer[1].t_count,
timerreg_4u.t_timer[0].t_limit, timerreg_4u.t_timer[1].t_limit);
#endif
#ifdef NOT_DEBUG
prom_printf("!");
#endif
statclock((struct clockframe *)cap);
#ifdef NOTDEF_DEBUG
return 1;
#endif
var = statvar;
do {
r = random() & (var - 1);
} while (r == 0);
newint = statmin + r;
if (schedhz)
if ((int)(--ci->ci_schedstate.spc_schedticks) <= 0) {
send_softint(-1, PIL_SCHED, schedint);
ci->ci_schedstate.spc_schedticks = statscheddiv;
}
stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
tmr_ustolim(newint)|TMR_LIM_IEN|TMR_LIM_RELOAD);
return (1);
}
int
schedintr(void *arg)
{
schedclock(curcpu()->ci_onproc);
return (1);
}
#endif