#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: e500_timer.c,v 1.8 2023/08/10 20:02:56 andvar Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#include <sys/intr.h>
#include <sys/cpu.h>
#include <uvm/uvm_extern.h>
#include <powerpc/spr.h>
#include <powerpc/booke/spr.h>
#include <powerpc/booke/cpuvar.h>
#include <powerpc/booke/e500reg.h>
#include <powerpc/booke/e500var.h>
#include <powerpc/booke/openpicreg.h>
uint32_t ticks_per_sec;
static u_long ns_per_tick;
static void init_ppcbooke_tc(void);
static u_int get_ppcbooke_timecount(struct timecounter *);
static struct timecounter ppcbooke_timecounter = {
.tc_get_timecount = get_ppcbooke_timecount,
.tc_counter_mask = ~0u,
.tc_name = "ppc_timebase",
.tc_quality = 100,
};
static inline void
openpic_write(struct cpu_softc *cpu, bus_size_t offset, uint32_t val)
{
return bus_space_write_4(cpu->cpu_bst, cpu->cpu_bsh,
OPENPIC_BASE + offset, val);
}
int
e500_clock_intr(void *v)
{
struct trapframe * const tf = v;
struct cpu_info * const ci = curcpu();
struct cpu_softc * const cpu = ci->ci_softc;
u_int nticks;
if (!cpu->cpu_ticks_per_clock_intr)
return 0;
const uint64_t now = mftb();
uint64_t latency = now - (ci->ci_lastintr + cpu->cpu_ticks_per_clock_intr);
#if 0
uint64_t orig_latency = latency;
#endif
if (now < ci->ci_lastintr + cpu->cpu_ticks_per_clock_intr)
latency = 0;
nticks = 1 + latency / cpu->cpu_ticks_per_clock_intr;
latency %= cpu->cpu_ticks_per_clock_intr;
#if 0
for (nticks = 1; latency >= cpu->cpu_ticks_per_clock_intr; nticks++) {
latency -= cpu->cpu_ticks_per_clock_intr;
}
#endif
ci->ci_ev_clock.ev_count++;
cpu->cpu_ev_late_clock.ev_count += nticks - 1;
#if 0
if (nticks > 10 || now - ci->ci_lastintr < 7 * cpu->cpu_ticks_per_clock_intr / 8)
printf("%s: nticks=%u lastintr=%#"PRIx64"(%#"PRIx64") now=%#"PRIx64" latency=%#"PRIx64" orig=%#"PRIx64"\n", __func__,
nticks, ci->ci_lastintr, now - latency, now, latency, orig_latency);
#endif
ci->ci_lastintr = now - latency;
ci->ci_lasttb = now;
wrtee(PSL_EE);
while (nticks-- > 0) {
hardclock(&tf->tf_cf);
}
wrtee(0);
tf->tf_srr1 &= ~PSL_POW;
return 1;
}
void
cpu_initclocks(void)
{
struct cpu_info * const ci = curcpu();
struct cpu_softc * const cpu = ci->ci_softc;
cpu->cpu_ticks_per_clock_intr = (ci->ci_data.cpu_cc_freq + hz/2 - 1) / hz;
ci->ci_lastintr = ci->ci_lasttb = mftb();
openpic_write(cpu, cpu->cpu_clock_gtbcr,
GTBCR_CI | cpu->cpu_ticks_per_clock_intr);
openpic_write(cpu, cpu->cpu_clock_gtbcr,
cpu->cpu_ticks_per_clock_intr);
if (CPU_IS_PRIMARY(ci))
init_ppcbooke_tc();
}
void
calc_delayconst(void)
{
struct cpu_info * const ci = curcpu();
ci->ci_data.cpu_cc_freq = board_info_get_number("timebase-frequency");
ticks_per_sec = (uint32_t)ci->ci_data.cpu_cc_freq;
ns_per_tick = 1000000000 / (u_int)ci->ci_data.cpu_cc_freq;
}
static u_int
get_ppcbooke_timecount(struct timecounter *tc)
{
return mftbl();
}
void
delay(unsigned int n)
{
uint64_t tb;
u_long tbh, tbl, scratch;
tb = mftb();
tb += (n * 1000ULL + ns_per_tick - 1) / ns_per_tick;
tbh = tb >> 32;
tbl = tb;
__asm volatile (
"1: mfspr %0,%4" "\n"
" cmplw %0,%1" "\n"
" blt 1b" "\n"
" bgt 2f" "\n"
" mfspr %0,%3" "\n"
" cmplw %0,%2" "\n"
" blt 1b" "\n"
"2:" "\n"
: "=&r"(scratch)
: "r"(tbh), "r"(tbl), "n"(SPR_TBL), "n"(SPR_TBU)
: "cr0");
}
void
setstatclockrate(int arg)
{
}
static void
init_ppcbooke_tc(void)
{
ppcbooke_timecounter.tc_frequency = curcpu()->ci_data.cpu_cc_freq;
tc_init(&ppcbooke_timecounter);
}