#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.19 2024/10/19 12:34:09 jmcneill Exp $");
#ifdef _KERNEL_OPT
#include "opt_ppcarch.h"
#endif
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/timetc.h>
#include <uvm/uvm_extern.h>
#include <powerpc/psl.h>
#include <powerpc/spr.h>
#if defined (PPC_OEA) || defined(PPC_OEA64) || defined (PPC_OEA64_BRIDGE)
#include <powerpc/oea/spr.h>
#elif defined (PPC_BOOKE)
#include <powerpc/booke/spr.h>
#elif defined (PPC_IBM4XX)
#include <powerpc/ibm4xx/spr.h>
#else
#error unknown powerpc variant
#endif
void decr_intr(struct clockframe *);
void init_powerpc_tc(void);
static u_int get_powerpc_timecount(struct timecounter *);
#ifdef PPC_OEA601
static u_int get_601_timecount(struct timecounter *);
#endif
uint32_t ticks_per_sec;
uint32_t ticks_per_msec;
uint32_t ns_per_tick;
uint32_t ticks_per_intr = 0;
#ifdef PPC_OEA601
static struct timecounter powerpc_601_timecounter = {
.tc_get_timecount = get_601_timecount,
.tc_poll_pps = 0,
.tc_counter_mask = 0x7fffffff,
.tc_frequency = 0,
.tc_name = "rtc",
.tc_quality = 100,
.tc_priv = NULL,
.tc_next = NULL
};
#endif
static struct timecounter powerpc_timecounter = {
.tc_get_timecount = get_powerpc_timecount,
.tc_poll_pps = 0,
.tc_counter_mask = 0x7fffffff,
.tc_frequency = 0,
.tc_name = "mftb",
.tc_quality = 100,
.tc_priv = NULL,
.tc_next = NULL
};
void
cpu_initclocks(void)
{
struct cpu_info * const ci = curcpu();
uint32_t msr;
ticks_per_intr = ticks_per_sec / hz;
cpu_timebase = ticks_per_sec;
#ifdef PPC_OEA601
if ((mfpvr() >> 16) == MPC601)
ci->ci_lasttb = rtc_nanosecs();
else
#endif
__asm volatile ("mftb %0" : "=r"(ci->ci_lasttb));
__asm volatile ("mtdec %0" :: "r"(ticks_per_intr));
init_powerpc_tc();
__asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0"
: "=r"(msr) : "K"(PSL_EE|PSL_RI));
}
void
setstatclockrate(int arg)
{
}
void
decr_intr(struct clockframe *cfp)
{
struct cpu_info * const ci = curcpu();
const register_t msr = mfmsr();
int pri;
u_long tb;
long ticks;
int nticks;
if (!ticks_per_intr)
return;
__asm ("mfdec %0" : "=r"(ticks));
for (nticks = 0; ticks < 0; nticks++)
ticks += ticks_per_intr;
__asm volatile ("mtdec %0" :: "r"(ticks));
ci->ci_data.cpu_nintr++;
ci->ci_ev_clock.ev_count++;
pri = splclock();
if (pri >= IPL_CLOCK) {
ci->ci_tickspending += nticks;
} else {
nticks += ci->ci_tickspending;
ci->ci_tickspending = 0;
#ifdef PPC_OEA601
if ((mfpvr() >> 16) == MPC601)
tb = rtc_nanosecs();
else
#endif
__asm volatile ("mftb %0" : "=r"(tb));
ci->ci_lasttb = tb + ticks - ticks_per_intr;
ci->ci_idepth++;
mtmsr(msr | PSL_EE);
while (--nticks > 0)
hardclock(cfp);
hardclock(cfp);
mtmsr(msr);
ci->ci_idepth--;
}
mtmsr(msr | PSL_EE);
splx(pri);
mtmsr(msr);
}
void
delay(unsigned int n)
{
#ifdef _ARCH_PPC64
uint64_t tb, scratch;
#else
uint64_t tb;
uint32_t tbh, tbl, scratch;
#ifdef PPC_OEA601
if ((mfpvr() >> 16) == MPC601) {
u_int32_t rtc[2];
mfrtc(rtc);
while (n >= 1000000) {
rtc[0]++;
n -= 1000000;
}
rtc[1] += (n * 1000);
if (rtc[1] >= 1000000000) {
rtc[0]++;
rtc[1] -= 1000000000;
}
__asm volatile ("1: mfspr %0,%3; cmplw %0,%1; blt 1b; bgt 2f;"
"mfspr %0,%4; cmplw %0,%2; blt 1b; 2:"
: "=&r"(scratch)
: "r"(rtc[0]), "r"(rtc[1]), "n"(SPR_RTCU_R), "n"(SPR_RTCL_R)
: "cr0");
} else
#endif
#endif
{
tb = mftb();
if (ticks_per_msec != 0 && n >= 1000) {
tb += (n / 1000ULL) * ticks_per_msec;
n = n % 1000;
}
tb += (n * 1000ULL + ns_per_tick - 1) / ns_per_tick;
#ifdef _ARCH_PPC64
__asm volatile ("1: mftb %0; cmpld %0,%1; blt 1b;"
: "=&r"(scratch) : "r"(tb)
: "cr0");
#else
tbh = tb >> 32;
tbl = tb;
__asm volatile ("1: mftbu %0; cmplw %0,%1; blt 1b; bgt 2f;"
"mftb %0; cmplw %0,%2; blt 1b; 2:"
: "=&r"(scratch) : "r"(tbh), "r"(tbl)
: "cr0");
#endif
}
}
static u_int
get_powerpc_timecount(struct timecounter *tc)
{
u_long tb;
int msr, scratch;
__asm volatile ("mfmsr %0; andi. %1,%0,%2; mtmsr %1"
: "=r"(msr), "=r"(scratch) : "K"((u_short)~PSL_EE));
tb = (u_int)(mftb() & 0x7fffffff);
mtmsr(msr);
return tb;
}
#ifdef PPC_OEA601
static u_int
get_601_timecount(struct timecounter *tc)
{
u_long tb;
int msr, scratch;
__asm volatile ("mfmsr %0; andi. %1,%0,%2; mtmsr %1"
: "=r"(msr), "=r"(scratch) : "K"((u_short)~PSL_EE));
tb = rtc_nanosecs();
mtmsr(msr);
return tb;
}
#endif
void
init_powerpc_tc(void)
{
struct timecounter *tc;
#ifdef PPC_OEA601
if ((mfpvr() >> 16) == MPC601) {
tc = &powerpc_601_timecounter;
} else
#endif
tc = &powerpc_timecounter;
tc->tc_frequency = ticks_per_sec;
tc_init(tc);
}