#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/clockintr.h>
#include <sys/sched.h>
#include <sys/stdint.h>
#include <sys/timetc.h>
#include <sys/atomic.h>
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/idprom.h>
#include <dev/clock_subr.h>
#include <dev/ic/mk48txxreg.h>
#include <sparc64/dev/sbusreg.h>
#include <dev/sbus/sbusvar.h>
#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>
#include <sparc64/dev/fhcvar.h>
extern u_int64_t cpu_clockrate;
struct clock_wenable_info {
bus_space_tag_t cwi_bt;
bus_space_handle_t cwi_bh;
bus_size_t cwi_size;
};
struct cfdriver clock_cd = {
NULL, "clock", DV_DULL
};
u_int tick_get_timecount(struct timecounter *);
struct timecounter tick_timecounter = {
.tc_get_timecount = tick_get_timecount,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_name = "tick",
.tc_quality = 0,
.tc_priv = NULL,
.tc_user = TC_TICK,
};
u_int sys_tick_get_timecount(struct timecounter *);
struct timecounter sys_tick_timecounter = {
.tc_get_timecount = sys_tick_get_timecount,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_name = "sys_tick",
.tc_quality = 1000,
.tc_priv = NULL,
.tc_user = TC_SYS_TICK,
};
void tick_start(void);
void sys_tick_start(void);
void stick_start(void);
int tickintr(void *);
int sys_tickintr(void *);
int stickintr(void *);
#define TICK_COUNT_MASK 0x7fffffffffffffff
uint64_t tick_nsec_cycle_ratio;
uint64_t tick_nsec_max;
void tick_rearm(void *, uint64_t);
void tick_trigger(void *);
const struct intrclock tick_intrclock = {
.ic_rearm = tick_rearm,
.ic_trigger = tick_trigger
};
#define STICK_COUNT_MASK 0x7fffffffffffffff
uint64_t sys_tick_nsec_cycle_ratio;
uint64_t sys_tick_nsec_max;
void sys_tick_rearm(void *, uint64_t);
void sys_tick_trigger(void *);
const struct intrclock sys_tick_intrclock = {
.ic_rearm = sys_tick_rearm,
.ic_trigger = sys_tick_trigger
};
void stick_rearm(void *, uint64_t);
void stick_trigger(void *);
const struct intrclock stick_intrclock = {
.ic_rearm = stick_rearm,
.ic_trigger = stick_trigger
};
void sparc64_raise_clockintr(void);
static struct intrhand level10 = {
.ih_fun = tickintr,
.ih_number = 1,
.ih_pil = 10,
.ih_name = "clock"
};
static int clockmatch_sbus(struct device *, void *, void *);
static void clockattach_sbus(struct device *, struct device *, void *);
static int clockmatch_ebus(struct device *, void *, void *);
static void clockattach_ebus(struct device *, struct device *, void *);
static int clockmatch_fhc(struct device *, void *, void *);
static void clockattach_fhc(struct device *, struct device *, void *);
static void clockattach(int, bus_space_tag_t, bus_space_handle_t);
const struct cfattach clock_sbus_ca = {
sizeof(struct device), clockmatch_sbus, clockattach_sbus
};
const struct cfattach clock_ebus_ca = {
sizeof(struct device), clockmatch_ebus, clockattach_ebus
};
const struct cfattach clock_fhc_ca = {
sizeof(struct device), clockmatch_fhc, clockattach_fhc
};
extern todr_chip_handle_t todr_handle;
static struct idprom *idprom;
int clock_bus_wenable(struct todr_chip_handle *, int);
void myetheraddr(u_char *);
struct idprom *getidprom(void);
static int
clockmatch_sbus(struct device *parent, void *cf, void *aux)
{
struct sbus_attach_args *sa = aux;
return (strcmp("eeprom", sa->sa_name) == 0);
}
static int
clockmatch_ebus(struct device *parent, void *cf, void *aux)
{
struct ebus_attach_args *ea = aux;
return (strcmp("eeprom", ea->ea_name) == 0);
}
static int
clockmatch_fhc(struct device *parent, void *cf, void *aux)
{
struct fhc_attach_args *fa = aux;
return (strcmp("eeprom", fa->fa_name) == 0);
}
static void
clockattach_sbus(struct device *parent, struct device *self, void *aux)
{
struct sbus_attach_args *sa = aux;
bus_space_tag_t bt = sa->sa_bustag;
int sz;
static struct clock_wenable_info cwi;
sz = 8192;
if (sbus_bus_map(bt,
sa->sa_slot,
(sa->sa_offset & ~NBPG),
sz,
BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
0, &cwi.cwi_bh) != 0) {
printf("%s: can't map register\n", self->dv_xname);
return;
}
clockattach(sa->sa_node, bt, cwi.cwi_bh);
cwi.cwi_bt = bt;
cwi.cwi_size = sz;
todr_handle->bus_cookie = &cwi;
todr_handle->todr_setwen = clock_bus_wenable;
}
int
clock_bus_wenable(struct todr_chip_handle *handle, int onoff)
{
int s, err = 0;
int prot;
volatile static int writers;
struct clock_wenable_info *cwi = handle->bus_cookie;
s = splhigh();
if (onoff)
prot = writers++ == 0 ? 1 : 0;
else
prot = --writers == 0 ? 1 : 0;
splx(s);
if (prot) {
err = bus_space_protect(cwi->cwi_bt, cwi->cwi_bh, cwi->cwi_size,
onoff ? 0 : BUS_SPACE_MAP_READONLY);
if (err)
printf("clock_wenable_info: WARNING -- cannot %s "
"page protection\n", onoff ? "disable" : "enable");
}
return (err);
}
static void
clockattach_ebus(struct device *parent, struct device *self, void *aux)
{
struct ebus_attach_args *ea = aux;
bus_space_tag_t bt;
int sz;
static struct clock_wenable_info cwi;
sz = ea->ea_regs[0].size;
if (ebus_bus_map(ea->ea_iotag, 0,
EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz, 0, 0, &cwi.cwi_bh) == 0) {
bt = ea->ea_iotag;
} else if (ebus_bus_map(ea->ea_memtag, 0,
EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz,
BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
0, &cwi.cwi_bh) == 0) {
bt = ea->ea_memtag;
} else {
printf("%s: can't map register\n", self->dv_xname);
return;
}
clockattach(ea->ea_node, bt, cwi.cwi_bh);
cwi.cwi_bt = bt;
cwi.cwi_size = sz;
todr_handle->bus_cookie = &cwi;
todr_handle->todr_setwen = (ea->ea_memtag == bt) ?
clock_bus_wenable : NULL;
}
static void
clockattach_fhc(struct device *parent, struct device *self, void *aux)
{
struct fhc_attach_args *fa = aux;
bus_space_tag_t bt = fa->fa_bustag;
int sz;
static struct clock_wenable_info cwi;
sz = 8192;
if (fhc_bus_map(bt, fa->fa_reg[0].fbr_slot,
(fa->fa_reg[0].fbr_offset & ~NBPG), fa->fa_reg[0].fbr_size,
BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, &cwi.cwi_bh) != 0) {
printf("%s: can't map register\n", self->dv_xname);
return;
}
clockattach(fa->fa_node, bt, cwi.cwi_bh);
cwi.cwi_bt = bt;
cwi.cwi_size = sz;
todr_handle->bus_cookie = &cwi;
todr_handle->todr_setwen = clock_bus_wenable;
}
static void
clockattach(int node, bus_space_tag_t bt, bus_space_handle_t bh)
{
char *model;
struct idprom *idp;
int h;
model = getpropstring(node, "model");
#ifdef DIAGNOSTIC
if (model == NULL)
panic("clockattach: no model property");
#endif
if ((todr_handle = mk48txx_attach(bt, bh, model, 1968)) == NULL)
panic("Can't attach %s tod clock", model);
#define IDPROM_OFFSET (8*1024 - 40)
if (idprom == NULL) {
idp = getidprom();
if (idp == NULL)
idp = (struct idprom *)(bus_space_vaddr(bt, bh) +
IDPROM_OFFSET);
idprom = idp;
} else
idp = idprom;
h = idp->id_machine << 24;
h |= idp->id_hostid[0] << 16;
h |= idp->id_hostid[1] << 8;
h |= idp->id_hostid[2];
hostid = h;
printf("\n");
}
struct idprom *
getidprom(void)
{
struct idprom *idp = NULL;
int node, n;
node = findroot();
if (getprop(node, "idprom", sizeof(*idp), &n, (void **)&idp) != 0)
return (NULL);
if (n != 1) {
free(idp, M_DEVBUF, 0);
return (NULL);
}
return (idp);
}
void
myetheraddr(u_char *cp)
{
struct idprom *idp;
if ((idp = idprom) == NULL) {
int node, n;
node = findroot();
if (getprop(node, "idprom", sizeof *idp, &n, (void **)&idp) ||
n != 1) {
printf("\nmyetheraddr: clock not setup yet, "
"and no idprom property in /\n");
return;
}
}
cp[0] = idp->id_ether[0];
cp[1] = idp->id_ether[1];
cp[2] = idp->id_ether[2];
cp[3] = idp->id_ether[3];
cp[4] = idp->id_ether[4];
cp[5] = idp->id_ether[5];
if (idprom == NULL)
free(idp, M_DEVBUF, 0);
}
void
cpu_initclocks(void)
{
u_int sys_tick_rate;
int impl = 0;
if (1000000 % hz) {
printf("cannot get %d Hz clock; using 100 Hz\n", hz);
hz = 100;
tick = 1000000 / hz;
tick_nsec = 1000000000 / hz;
}
stathz = hz;
profhz = stathz * 10;
statclock_is_randomized = 1;
if (!cpu_clockrate)
cpu_clockrate = 200000000;
tick_timecounter.tc_frequency = cpu_clockrate;
tc_init(&tick_timecounter);
if (CPU_ISSUN4U || CPU_ISSUN4US)
impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;
sys_tick_rate = getpropint(findroot(), "stick-frequency", 0);
if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) {
sys_tick_timecounter.tc_frequency = sys_tick_rate;
tc_init(&sys_tick_timecounter);
}
struct cpu_info *ci;
intr_establish(&level10);
evcount_percpu(&level10.ih_count);
if (sys_tick_rate > 0) {
sys_tick_nsec_cycle_ratio =
sys_tick_rate * (1ULL << 32) / 1000000000;
sys_tick_nsec_max = UINT64_MAX / sys_tick_nsec_cycle_ratio;
if (impl == IMPL_HUMMINGBIRD) {
level10.ih_fun = stickintr;
cpu_start_clock = stick_start;
} else {
level10.ih_fun = sys_tickintr;
cpu_start_clock = sys_tick_start;
}
} else {
tick_nsec_cycle_ratio =
cpu_clockrate * (1ULL << 32) / 1000000000;
tick_nsec_max = UINT64_MAX / tick_nsec_cycle_ratio;
level10.ih_fun = tickintr;
cpu_start_clock = tick_start;
}
for (ci = cpus; ci != NULL; ci = ci->ci_next)
memcpy(&ci->ci_tickintr, &level10, sizeof(level10));
}
void
cpu_startclock(void)
{
cpu_start_clock();
}
void
setstatclockrate(int newhz)
{
}
int
tickintr(void *cap)
{
clockintr_dispatch(cap);
evcount_inc(&level10.ih_count);
return (1);
}
int
sys_tickintr(void *cap)
{
clockintr_dispatch(cap);
evcount_inc(&level10.ih_count);
return (1);
}
int
stickintr(void *cap)
{
clockintr_dispatch(cap);
evcount_inc(&level10.ih_count);
return (1);
}
void
tick_start(void)
{
tick_enable();
clockintr_cpu_init(&tick_intrclock);
clockintr_trigger();
}
void
tick_rearm(void *unused, uint64_t nsecs)
{
uint64_t s, t0;
uint32_t cycles;
if (nsecs > tick_nsec_max)
nsecs = tick_nsec_max;
cycles = (nsecs * tick_nsec_cycle_ratio) >> 32;
s = intr_disable();
t0 = tick();
tickcmpr_set((t0 + cycles) & TICK_COUNT_MASK);
if (cycles <= ((tick() - t0) & TICK_COUNT_MASK))
sparc64_raise_clockintr();
intr_restore(s);
}
void
tick_trigger(void *unused)
{
sparc64_raise_clockintr();
}
void
sys_tick_start(void)
{
if (CPU_ISSUN4U || CPU_ISSUN4US) {
tick_enable();
sys_tick_enable();
}
clockintr_cpu_init(&sys_tick_intrclock);
clockintr_trigger();
}
void
sys_tick_rearm(void *unused, uint64_t nsecs)
{
uint64_t s, t0;
uint32_t cycles;
if (nsecs > sys_tick_nsec_max)
nsecs = sys_tick_nsec_max;
cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32;
s = intr_disable();
t0 = sys_tick();
sys_tickcmpr_set((t0 + cycles) & STICK_COUNT_MASK);
if (cycles <= ((sys_tick() - t0) & STICK_COUNT_MASK))
sparc64_raise_clockintr();
intr_restore(s);
}
void
sys_tick_trigger(void *unused)
{
sparc64_raise_clockintr();
}
void
stick_start(void)
{
tick_enable();
clockintr_cpu_init(&stick_intrclock);
clockintr_trigger();
}
void
stick_rearm(void *unused, uint64_t nsecs)
{
uint64_t s, t0;
uint32_t cycles;
if (nsecs > sys_tick_nsec_max)
nsecs = sys_tick_nsec_max;
cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32;
s = intr_disable();
t0 = stick();
stickcmpr_set((t0 + cycles) & STICK_COUNT_MASK);
if (cycles <= ((stick() - t0) & STICK_COUNT_MASK))
sparc64_raise_clockintr();
intr_restore(s);
}
void
stick_trigger(void *unused)
{
sparc64_raise_clockintr();
}
u_int
tick_get_timecount(struct timecounter *tc)
{
u_int64_t tick;
__asm volatile("rd %%tick, %0" : "=r" (tick));
return (tick & ~0u);
}
u_int
sys_tick_get_timecount(struct timecounter *tc)
{
u_int64_t tick;
__asm volatile("rd %%sys_tick, %0" : "=r" (tick));
return (tick & ~0u);
}
void
sparc64_raise_clockintr(void)
{
send_softint(PIL_CLOCK, &curcpu()->ci_tickintr);
}