2005-12-31 13:16:00 +00:00
|
|
|
/* CLOCK.C (c) Copyright Jan Jaeger, 2000-2006 */
|
2005-12-04 11:38:43 +00:00
|
|
|
/* TOD Clock functions */
|
|
|
|
|
|
|
|
|
|
/* The emulated hardware clock is based on the host clock, adjusted */
|
|
|
|
|
/* by means of an offset and a steering rate. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "hstdinc.h"
|
|
|
|
|
|
|
|
|
|
#if !defined(_HENGINE_DLL_)
|
|
|
|
|
#define _HENGINE_DLL_
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "hercules.h"
|
|
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
#include "opcode.h"
|
|
|
|
|
|
|
|
|
|
#include "inline.h"
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
#if !defined(_CLOCK_C_)
|
|
|
|
|
#define _CLOCK_C_
|
|
|
|
|
|
|
|
|
|
#include "clock.h"
|
|
|
|
|
|
|
|
|
|
static double hw_steering = 0.0; /* Current TOD clock steering rate */
|
|
|
|
|
|
|
|
|
|
static U64 hw_episode; /* TOD of start of steering episode */
|
|
|
|
|
|
|
|
|
|
static S64 hw_offset = 0; /* Current offset between TOD and HW */
|
|
|
|
|
|
2005-12-06 20:41:42 +00:00
|
|
|
static int clock_state = CC_CLOCK_SET;
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
static CSR old;
|
|
|
|
|
static CSR new;
|
|
|
|
|
static CSR *current = &new;
|
|
|
|
|
|
|
|
|
|
void csr_reset()
|
|
|
|
|
{
|
|
|
|
|
new.start_time = 0;
|
|
|
|
|
new.base_offset = 0;
|
|
|
|
|
new.fine_s_rate = 0;
|
|
|
|
|
new.gross_s_rate = 0;
|
|
|
|
|
current = &new;
|
|
|
|
|
old = new;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-29 13:43:42 +00:00
|
|
|
static U64 universal_tod;
|
2005-12-04 11:38:43 +00:00
|
|
|
static U64 universal_clock(void) /* really: any clock used as a base */
|
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
|
|
gettimeofday (&tv, NULL);
|
|
|
|
|
|
|
|
|
|
/* Load number of seconds since 00:00:00 01 Jan 1970 */
|
|
|
|
|
universal_tod = (U64)tv.tv_sec;
|
|
|
|
|
|
|
|
|
|
universal_tod += SECONDS_IN_SEVENTY_YEARS;
|
|
|
|
|
|
|
|
|
|
/* Convert to microseconds */
|
|
|
|
|
universal_tod = (universal_tod * 1000000) + tv.tv_usec;
|
|
|
|
|
|
|
|
|
|
/* Shift left 4 bits so that bits 0-7=TOD Clock Epoch,
|
|
|
|
|
bits 8-59=TOD Clock bits 0-51, bits 60-63=zero */
|
|
|
|
|
universal_tod <<= 4;
|
|
|
|
|
|
|
|
|
|
return universal_tod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The hercules hardware clock, based on the universal clock, but */
|
|
|
|
|
/* running at its own speed as optionally set by set_tod_steering() */
|
2005-12-28 19:32:55 +00:00
|
|
|
/* The hardware clock returns a unique value */
|
2005-12-29 13:43:42 +00:00
|
|
|
static U64 hw_tod = 0;
|
2005-12-04 11:38:43 +00:00
|
|
|
U64 hw_clock(void)
|
|
|
|
|
{
|
|
|
|
|
U64 base_tod;
|
|
|
|
|
|
|
|
|
|
/* Apply hardware offset, this is the offset achieved by all
|
|
|
|
|
previous steering episodes */
|
|
|
|
|
base_tod = universal_clock() + hw_offset;
|
|
|
|
|
|
|
|
|
|
/* Apply the steering offset from the current steering episode */
|
|
|
|
|
base_tod += (S64)(base_tod - hw_episode) * hw_steering;
|
|
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
/* Ensure that the clock returns a unique value */
|
2005-12-29 13:43:42 +00:00
|
|
|
if(hw_tod < base_tod)
|
|
|
|
|
hw_tod = base_tod;
|
2005-12-28 19:32:55 +00:00
|
|
|
else
|
2005-12-29 13:43:42 +00:00
|
|
|
hw_tod += 0x10;
|
|
|
|
|
|
|
|
|
|
return hw_tod;
|
2005-12-04 11:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* set_tod_steering(double) sets a new steering rate. */
|
|
|
|
|
/* When a new steering episode begins, the offset is adjusted, */
|
|
|
|
|
/* and the new steering rate takes effect */
|
|
|
|
|
void set_tod_steering(double steering)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
2005-12-29 13:43:42 +00:00
|
|
|
hw_offset = hw_clock() - universal_tod;
|
2005-12-04 11:38:43 +00:00
|
|
|
hw_steering = steering;
|
2005-12-29 13:43:42 +00:00
|
|
|
hw_episode = hw_tod;
|
2005-12-04 11:38:43 +00:00
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
/* Start a new episode */
|
|
|
|
|
static inline void start_new_episode()
|
|
|
|
|
{
|
2005-12-29 13:43:42 +00:00
|
|
|
hw_offset = hw_clock() - universal_tod;
|
|
|
|
|
hw_episode = hw_tod;
|
|
|
|
|
new.start_time = hw_episode;
|
|
|
|
|
hw_steering = ldexp(2,-44) * (S32)(new.fine_s_rate + new.gross_s_rate);
|
2005-12-28 19:32:55 +00:00
|
|
|
current = &new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Prepare for a new episode */
|
|
|
|
|
static inline void prepare_new_episode()
|
|
|
|
|
{
|
|
|
|
|
if(current == &new)
|
|
|
|
|
{
|
|
|
|
|
old = new;
|
|
|
|
|
current = &old;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
/* Ajust the epoch for all active cpu's in the configuration */
|
|
|
|
|
static U64 adjust_epoch_cpu_all(U64 epoch)
|
|
|
|
|
{
|
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
|
|
/* Update the TOD clock of all CPU's in the configuration
|
|
|
|
|
as we simulate 1 shared TOD clock, and do not support the
|
|
|
|
|
TOD clock sync check */
|
|
|
|
|
for (cpu = 0; cpu < MAX_CPU; cpu++)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.cpulock[cpu]);
|
|
|
|
|
if (IS_CPU_ONLINE(cpu))
|
|
|
|
|
sysblk.regs[cpu]->tod_epoch = epoch;
|
|
|
|
|
release_lock(&sysblk.cpulock[cpu]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return epoch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double get_tod_steering(void)
|
|
|
|
|
{
|
|
|
|
|
return hw_steering;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_tod_epoch(S64 epoch)
|
|
|
|
|
{
|
2005-12-28 19:32:55 +00:00
|
|
|
csr_reset();
|
2005-12-04 11:38:43 +00:00
|
|
|
tod_epoch = adjust_epoch_cpu_all(epoch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ajust_tod_epoch(S64 epoch)
|
|
|
|
|
{
|
2005-12-28 19:32:55 +00:00
|
|
|
csr_reset();
|
2005-12-04 11:38:43 +00:00
|
|
|
tod_epoch += epoch;
|
|
|
|
|
adjust_epoch_cpu_all(tod_epoch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S64 get_tod_epoch()
|
|
|
|
|
{
|
|
|
|
|
return tod_epoch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
static void set_gross_steering_rate(S32 gsr)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
prepare_new_episode();
|
|
|
|
|
new.gross_s_rate = gsr;
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void set_fine_steering_rate(S32 fsr)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
prepare_new_episode();
|
|
|
|
|
new.fine_s_rate = fsr;
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void set_tod_offset(S64 offset)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
prepare_new_episode();
|
|
|
|
|
new.base_offset = offset;
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void adjust_tod_offset(S64 offset)
|
|
|
|
|
{
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
prepare_new_episode();
|
|
|
|
|
new.base_offset = old.base_offset + offset;
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
|
/* Update TOD clock */
|
|
|
|
|
/* */
|
|
|
|
|
/* This function updates the TOD clock. */
|
|
|
|
|
/* */
|
|
|
|
|
/* This function is called by timer_update_thread and by cpu_thread */
|
|
|
|
|
/* instructions that manipulate any of the timer related entities */
|
|
|
|
|
/* (clock comparator, cpu timer and interval timer). */
|
|
|
|
|
/* */
|
|
|
|
|
/* Internal function `check_timer_event' is called which will signal */
|
|
|
|
|
/* any timer related interrupts to the appropriate cpu_thread. */
|
|
|
|
|
/* */
|
|
|
|
|
/* Callers *must* own the todlock and *must not* own the intlock. */
|
|
|
|
|
/* */
|
|
|
|
|
/* update_tod_clock() returns the tod delta, by which the cpu timer */
|
|
|
|
|
/* has been adjusted. */
|
|
|
|
|
/* */
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
2005-12-30 11:23:15 +00:00
|
|
|
static U64 tod_timer;
|
2005-12-04 11:38:43 +00:00
|
|
|
U64 update_tod_clock(void)
|
|
|
|
|
{
|
|
|
|
|
U64 new_clock,
|
|
|
|
|
tod_delta;
|
|
|
|
|
|
|
|
|
|
new_clock = hw_clock();
|
2005-12-30 11:23:15 +00:00
|
|
|
tod_delta = new_clock - tod_timer;
|
|
|
|
|
tod_timer = new_clock;
|
2005-12-04 11:38:43 +00:00
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
/* If we are in the old episode, and the new episode has arrived
|
|
|
|
|
then we must take action to start the new episode */
|
2005-12-29 13:43:42 +00:00
|
|
|
if(current == &old)
|
2005-12-28 19:32:55 +00:00
|
|
|
start_new_episode();
|
|
|
|
|
|
|
|
|
|
/* Set the clock to the new updated value with offset applied */
|
|
|
|
|
tod_clock = new_clock + current->base_offset;
|
|
|
|
|
|
|
|
|
|
/* Update the timers and check if either a clock related event has
|
|
|
|
|
become pending */
|
2005-12-04 11:38:43 +00:00
|
|
|
update_cpu_timer(tod_delta);
|
|
|
|
|
|
|
|
|
|
return tod_delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2005-12-28 19:32:55 +00:00
|
|
|
#if defined(FEATURE_TOD_CLOCK_STEERING)
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(set_gross_s_rate) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
S32 gsr;
|
2005-12-29 18:55:26 +00:00
|
|
|
gsr = ARCH_DEP(vfetch4) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
set_gross_steering_rate(gsr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(set_fine_s_rate) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
S32 fsr;
|
2005-12-29 18:55:26 +00:00
|
|
|
fsr = ARCH_DEP(vfetch4) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
set_fine_steering_rate(fsr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(set_tod_offset) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
S64 offset;
|
2005-12-29 18:55:26 +00:00
|
|
|
offset = ARCH_DEP(vfetch8) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
set_tod_offset(offset >> 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(adjust_tod_offset) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
S64 offset;
|
2005-12-29 18:55:26 +00:00
|
|
|
offset = ARCH_DEP(vfetch8) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
adjust_tod_offset(offset >> 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(query_physical_clock) (REGS *regs)
|
|
|
|
|
{
|
2005-12-29 18:55:26 +00:00
|
|
|
ARCH_DEP(vstore8) (universal_clock() << 8, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(query_steering_information) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
PTFFQSI qsi;
|
2005-12-29 13:43:42 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
STORE_DW(qsi.physclk, universal_clock() << 8);
|
2005-12-28 19:43:44 +00:00
|
|
|
STORE_DW(qsi.oldestart, old.start_time << 8);
|
|
|
|
|
STORE_DW(qsi.oldebase, old.base_offset << 8);
|
2005-12-28 19:32:55 +00:00
|
|
|
STORE_FW(qsi.oldfsr, old.fine_s_rate );
|
|
|
|
|
STORE_FW(qsi.oldgsr, old.gross_s_rate );
|
2005-12-28 19:43:44 +00:00
|
|
|
STORE_DW(qsi.newestart, new.start_time << 8);
|
|
|
|
|
STORE_DW(qsi.newebase, new.base_offset << 8);
|
2005-12-28 19:32:55 +00:00
|
|
|
STORE_FW(qsi.newfsr, new.fine_s_rate );
|
|
|
|
|
STORE_FW(qsi.newgsr, new.gross_s_rate );
|
2005-12-29 13:43:42 +00:00
|
|
|
release_lock(&sysblk.todlock);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
2005-12-29 18:55:26 +00:00
|
|
|
ARCH_DEP(vstorec) (&qsi, sizeof(qsi)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(query_tod_offset) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
PTFFQTO qto;
|
2005-12-29 13:43:42 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
STORE_DW(qto.todoff, (hw_clock() - universal_tod) << 8);
|
|
|
|
|
STORE_DW(qto.physclk, universal_tod << 8);
|
2005-12-28 19:43:44 +00:00
|
|
|
STORE_DW(qto.ltodoff, current->base_offset << 8);
|
|
|
|
|
STORE_DW(qto.todepoch, regs->tod_epoch << 8);
|
2005-12-29 13:43:42 +00:00
|
|
|
release_lock(&sysblk.todlock);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
2005-12-29 18:55:26 +00:00
|
|
|
ARCH_DEP(vstorec) (&qto, sizeof(qto)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ARCH_DEP(query_available_functions) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
PTFFQAF qaf;
|
2005-12-28 21:04:29 +00:00
|
|
|
STORE_FW(qaf.sb[0] , 0xF0000000); /* Functions 0x00..0x1F */
|
|
|
|
|
STORE_FW(qaf.sb[1] , 0x00000000); /* Functions 0x20..0x3F */
|
|
|
|
|
STORE_FW(qaf.sb[2] , 0xF0000000); /* Functions 0x40..0x5F */
|
|
|
|
|
STORE_FW(qaf.sb[3] , 0x00000000); /* Functions 0x60..0x7F */
|
2005-12-28 19:32:55 +00:00
|
|
|
|
2005-12-29 18:55:26 +00:00
|
|
|
ARCH_DEP(vstorec) (&qaf, sizeof(qaf)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
2005-12-28 19:32:55 +00:00
|
|
|
}
|
|
|
|
|
#endif /*defined(FEATURE_TOD_CLOCK_STEERING)*/
|
2005-12-04 11:38:43 +00:00
|
|
|
|
|
|
|
|
#if !defined(_GEN_ARCH)
|
|
|
|
|
|
|
|
|
|
#if defined(_ARCHMODE2)
|
|
|
|
|
#define _GEN_ARCH _ARCHMODE2
|
|
|
|
|
#include "clock.c"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(_ARCHMODE3)
|
|
|
|
|
#undef _GEN_ARCH
|
|
|
|
|
#define _GEN_ARCH _ARCHMODE3
|
|
|
|
|
#include "clock.c"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|