2010-05-10 23:38:28 +00:00
|
|
|
/* CLOCK.C (c) Copyright Jan Jaeger, 2000-2010 */
|
2005-12-04 11:38:43 +00:00
|
|
|
/* TOD Clock functions */
|
2010-05-10 23:38:28 +00:00
|
|
|
/* */
|
|
|
|
|
/* Released under "The Q Public License Version 1" */
|
|
|
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
|
|
|
/* Hercules. */
|
2005-12-04 11:38:43 +00:00
|
|
|
|
2006-12-08 09:43:35 +00:00
|
|
|
// $Id$
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
/* 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"
|
|
|
|
|
|
2006-11-09 00:13:39 +00:00
|
|
|
#include "sr.h"
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
#if !defined(_CLOCK_C_)
|
|
|
|
|
#define _CLOCK_C_
|
|
|
|
|
|
|
|
|
|
#include "clock.h"
|
|
|
|
|
|
2005-12-31 20:14:56 +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-31 20:14:56 +00:00
|
|
|
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 */
|
|
|
|
|
// static U64 hw_tod = 0; /* Globally defined in clock.h */
|
2005-12-04 11:38:43 +00:00
|
|
|
|
2006-01-01 21:37:32 +00:00
|
|
|
static inline U64 hw_adjust(U64 base_tod)
|
|
|
|
|
{
|
2005-12-04 11:38:43 +00:00
|
|
|
/* Apply hardware offset, this is the offset achieved by all
|
|
|
|
|
previous steering episodes */
|
2006-01-01 21:37:32 +00:00
|
|
|
base_tod += hw_offset;
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
/* 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)
|
2006-01-01 21:37:32 +00:00
|
|
|
return base_tod;
|
2005-12-28 19:32:55 +00:00
|
|
|
else
|
2007-11-21 22:55:49 +00:00
|
|
|
return hw_tod += 0x10;
|
2006-01-01 21:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
U64 hw_clock(void)
|
|
|
|
|
{
|
|
|
|
|
U64 temp_tod;
|
|
|
|
|
|
2008-08-22 04:43:50 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
|
2006-01-01 21:37:32 +00:00
|
|
|
/* Get the time of day (GMT) */
|
|
|
|
|
temp_tod = universal_clock();
|
|
|
|
|
|
|
|
|
|
/* Ajust speed and ensure uniqueness */
|
|
|
|
|
hw_tod = hw_adjust(temp_tod);
|
|
|
|
|
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
|
|
|
|
|
return hw_tod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static U64 hw_clock_l(void)
|
|
|
|
|
{
|
|
|
|
|
hw_tod = hw_adjust(universal_clock());
|
2005-12-29 13:43:42 +00:00
|
|
|
|
|
|
|
|
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);
|
2006-01-01 21:37:32 +00:00
|
|
|
hw_offset = hw_clock_l() - universal_tod;
|
2005-12-29 13:43:42 +00:00
|
|
|
hw_episode = hw_tod;
|
2005-12-31 20:14:56 +00:00
|
|
|
hw_steering = steering;
|
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-31 20:14:56 +00:00
|
|
|
hw_offset = hw_tod - universal_tod;
|
2005-12-29 13:43:42 +00:00
|
|
|
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)
|
|
|
|
|
{
|
2006-01-02 21:36:39 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
2005-12-28 19:32:55 +00:00
|
|
|
csr_reset();
|
2006-01-02 21:36:39 +00:00
|
|
|
tod_epoch = epoch;
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
adjust_epoch_cpu_all(epoch);
|
2005-12-04 11:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-02-04 01:06:56 +00:00
|
|
|
void adjust_tod_epoch(S64 epoch)
|
2005-12-04 11:38:43 +00:00
|
|
|
{
|
2006-01-02 21:36:39 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
2005-12-28 19:32:55 +00:00
|
|
|
csr_reset();
|
2005-12-04 11:38:43 +00:00
|
|
|
tod_epoch += epoch;
|
2006-01-02 21:36:39 +00:00
|
|
|
release_lock(&sysblk.todlock);
|
2005-12-04 11:38:43 +00:00
|
|
|
adjust_epoch_cpu_all(tod_epoch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-01 21:37:32 +00:00
|
|
|
void set_tod_clock(U64 tod)
|
|
|
|
|
{
|
2006-01-02 21:36:39 +00:00
|
|
|
set_tod_epoch(tod - hw_clock());
|
2006-01-01 21:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
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-31 20:14:56 +00:00
|
|
|
/* The cpu timer is internally kept as an offset to the hw_clock()
|
|
|
|
|
* the cpu timer counts down as the clock approaches the timer epoch
|
|
|
|
|
*/
|
|
|
|
|
void set_cpu_timer(REGS *regs, S64 timer)
|
|
|
|
|
{
|
|
|
|
|
regs->cpu_timer = (timer >> 8) + hw_clock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-08 13:07:32 +00:00
|
|
|
S64 cpu_timer(REGS *regs)
|
2005-12-31 20:14:56 +00:00
|
|
|
{
|
|
|
|
|
S64 timer;
|
|
|
|
|
timer = (regs->cpu_timer - hw_clock()) << 8;
|
|
|
|
|
return timer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-02 21:36:39 +00:00
|
|
|
U64 tod_clock(REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
U64 current_tod;
|
|
|
|
|
|
|
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
|
|
|
|
|
current_tod = hw_clock_l();
|
|
|
|
|
|
|
|
|
|
/* If we are in the old episode, and the new episode has arrived
|
|
|
|
|
then we must take action to start the new episode */
|
|
|
|
|
if(current == &old)
|
|
|
|
|
start_new_episode();
|
|
|
|
|
|
|
|
|
|
/* Set the clock to the new updated value with offset applied */
|
|
|
|
|
current_tod += current->base_offset;
|
|
|
|
|
|
|
|
|
|
tod_value = current_tod;
|
|
|
|
|
|
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
|
|
|
|
|
|
return current_tod + regs->tod_epoch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-10 20:34:16 +00:00
|
|
|
#if defined(_FEATURE_INTERVAL_TIMER)
|
|
|
|
|
|
|
|
|
|
|
2006-01-13 11:00:39 +00:00
|
|
|
#if defined(_FEATURE_ECPSVM)
|
2006-01-10 20:34:16 +00:00
|
|
|
static inline S32 ecps_vtimer(REGS *regs)
|
|
|
|
|
{
|
2006-01-14 15:33:29 +00:00
|
|
|
return (S32)TOD_TO_ITIMER((S64)(regs->ecps_vtimer - hw_clock()));
|
2006-01-10 20:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void set_ecps_vtimer(REGS *regs, S32 vtimer)
|
|
|
|
|
{
|
2006-01-14 15:33:29 +00:00
|
|
|
regs->ecps_vtimer = (U64)(hw_clock() + ITIMER_TO_TOD(vtimer));
|
2006-01-10 20:34:16 +00:00
|
|
|
regs->ecps_oldtmr = vtimer;
|
|
|
|
|
}
|
|
|
|
|
#endif /*defined(_FEATURE_ECPSVM)*/
|
|
|
|
|
|
|
|
|
|
|
2006-01-08 13:07:32 +00:00
|
|
|
S32 int_timer(REGS *regs)
|
2006-01-07 16:12:14 +00:00
|
|
|
{
|
2006-01-13 23:00:21 +00:00
|
|
|
return (S32)TOD_TO_ITIMER((S64)(regs->int_timer - hw_clock()));
|
2006-01-07 16:12:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_int_timer(REGS *regs, S32 itimer)
|
|
|
|
|
{
|
2006-01-14 15:33:29 +00:00
|
|
|
regs->int_timer = (U64)(hw_clock() + ITIMER_TO_TOD(itimer));
|
2006-01-08 13:07:32 +00:00
|
|
|
regs->old_timer = itimer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-14 14:16:20 +00:00
|
|
|
int chk_int_timer(REGS *regs)
|
2006-01-08 13:07:32 +00:00
|
|
|
{
|
|
|
|
|
S32 itimer;
|
2006-01-14 14:16:20 +00:00
|
|
|
int pending = 0;
|
|
|
|
|
|
2006-01-13 20:01:55 +00:00
|
|
|
itimer = int_timer(regs);
|
2006-01-08 13:07:32 +00:00
|
|
|
if(itimer < 0 && regs->old_timer >= 0)
|
2006-01-14 14:16:20 +00:00
|
|
|
{
|
2006-01-08 13:07:32 +00:00
|
|
|
ON_IC_ITIMER(regs);
|
2006-01-14 14:16:20 +00:00
|
|
|
pending = 1;
|
|
|
|
|
}
|
2006-01-13 11:00:39 +00:00
|
|
|
#if defined(_FEATURE_ECPSVM)
|
|
|
|
|
if(regs->ecps_vtmrpt)
|
|
|
|
|
{
|
|
|
|
|
itimer = ecps_vtimer(regs);
|
|
|
|
|
if(itimer < 0 && regs->ecps_oldtmr >= 0)
|
2006-01-14 14:16:20 +00:00
|
|
|
{
|
2006-01-13 11:00:39 +00:00
|
|
|
ON_IC_ECPSVTIMER(regs);
|
2006-10-02 17:14:43 +00:00
|
|
|
pending += 2;
|
2006-01-14 14:16:20 +00:00
|
|
|
}
|
2006-01-13 11:00:39 +00:00
|
|
|
}
|
2006-01-10 20:34:16 +00:00
|
|
|
#endif /*defined(_FEATURE_ECPSVM)*/
|
2006-01-14 14:16:20 +00:00
|
|
|
|
|
|
|
|
return pending;
|
2006-01-07 16:12:14 +00:00
|
|
|
}
|
2006-01-10 20:34:16 +00:00
|
|
|
#endif /*defined(_FEATURE_INTERVAL_TIMER)*/
|
2006-01-07 16:12:14 +00:00
|
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
/* */
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
2006-01-02 21:36:39 +00:00
|
|
|
// static U64 tod_value;
|
2005-12-04 11:38:43 +00:00
|
|
|
U64 update_tod_clock(void)
|
|
|
|
|
{
|
2006-01-17 19:02:50 +00:00
|
|
|
U64 new_clock;
|
2005-12-04 11:38:43 +00:00
|
|
|
|
2006-01-01 21:37:32 +00:00
|
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
|
|
|
|
|
|
new_clock = hw_clock_l();
|
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 */
|
2006-01-01 21:37:32 +00:00
|
|
|
new_clock += current->base_offset;
|
2006-01-02 21:36:39 +00:00
|
|
|
tod_value = new_clock;
|
2006-01-01 21:37:32 +00:00
|
|
|
|
|
|
|
|
release_lock(&sysblk.todlock);
|
2005-12-28 19:32:55 +00:00
|
|
|
|
|
|
|
|
/* Update the timers and check if either a clock related event has
|
|
|
|
|
become pending */
|
2006-01-14 15:33:29 +00:00
|
|
|
update_cpu_timer();
|
2005-12-04 11:38:43 +00:00
|
|
|
|
2006-01-01 21:37:32 +00:00
|
|
|
return new_clock;
|
2005-12-04 11:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
2006-11-09 00:13:39 +00:00
|
|
|
#define SR_SYS_CLOCK_CURRENT_CSR ( SR_SYS_CLOCK | 0x001 )
|
|
|
|
|
#define SR_SYS_CLOCK_UNIVERSAL_TOD ( SR_SYS_CLOCK | 0x002 )
|
|
|
|
|
#define SR_SYS_CLOCK_HW_STEERING ( SR_SYS_CLOCK | 0x004 )
|
|
|
|
|
#define SR_SYS_CLOCK_HW_EPISODE ( SR_SYS_CLOCK | 0x005 )
|
|
|
|
|
#define SR_SYS_CLOCK_HW_OFFSET ( SR_SYS_CLOCK | 0x006 )
|
|
|
|
|
|
|
|
|
|
#define SR_SYS_CLOCK_OLD_CSR ( SR_SYS_CLOCK | 0x100 )
|
|
|
|
|
#define SR_SYS_CLOCK_OLD_CSR_START_TIME ( SR_SYS_CLOCK | 0x101 )
|
|
|
|
|
#define SR_SYS_CLOCK_OLD_CSR_BASE_OFFSET ( SR_SYS_CLOCK | 0x102 )
|
|
|
|
|
#define SR_SYS_CLOCK_OLD_CSR_FINE_S_RATE ( SR_SYS_CLOCK | 0x103 )
|
|
|
|
|
#define SR_SYS_CLOCK_OLD_CSR_GROSS_S_RATE ( SR_SYS_CLOCK | 0x104 )
|
|
|
|
|
|
|
|
|
|
#define SR_SYS_CLOCK_NEW_CSR ( SR_SYS_CLOCK | 0x200 )
|
|
|
|
|
#define SR_SYS_CLOCK_NEW_CSR_START_TIME ( SR_SYS_CLOCK | 0x201 )
|
|
|
|
|
#define SR_SYS_CLOCK_NEW_CSR_BASE_OFFSET ( SR_SYS_CLOCK | 0x202 )
|
|
|
|
|
#define SR_SYS_CLOCK_NEW_CSR_FINE_S_RATE ( SR_SYS_CLOCK | 0x203 )
|
|
|
|
|
#define SR_SYS_CLOCK_NEW_CSR_GROSS_S_RATE ( SR_SYS_CLOCK | 0x204 )
|
|
|
|
|
|
|
|
|
|
int clock_hsuspend(void *file)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char buf[SR_MAX_STRING_LENGTH];
|
|
|
|
|
|
|
|
|
|
i = (current == &new);
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_CURRENT_CSR, i, sizeof(i));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_UNIVERSAL_TOD, universal_tod, sizeof(universal_tod));
|
|
|
|
|
snprintf(buf, sizeof(buf), "%f", hw_steering);
|
|
|
|
|
SR_WRITE_STRING(file, SR_SYS_CLOCK_HW_STEERING, buf);
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_HW_EPISODE, hw_episode, sizeof(hw_episode));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_HW_OFFSET, hw_offset, sizeof(hw_offset));
|
|
|
|
|
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_START_TIME, old.start_time, sizeof(old.start_time));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_BASE_OFFSET, old.base_offset, sizeof(old.base_offset));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_FINE_S_RATE, old.fine_s_rate, sizeof(old.fine_s_rate));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_GROSS_S_RATE, old.gross_s_rate, sizeof(old.gross_s_rate));
|
|
|
|
|
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_START_TIME, new.start_time, sizeof(new.start_time));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_BASE_OFFSET, new.base_offset, sizeof(new.base_offset));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_FINE_S_RATE, new.fine_s_rate, sizeof(new.fine_s_rate));
|
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_GROSS_S_RATE, new.gross_s_rate, sizeof(new.gross_s_rate));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int clock_hresume(void *file)
|
|
|
|
|
{
|
|
|
|
|
size_t key, len;
|
|
|
|
|
int i;
|
|
|
|
|
float f;
|
|
|
|
|
char buf[SR_MAX_STRING_LENGTH];
|
|
|
|
|
|
|
|
|
|
memset(&old, 0, sizeof(CSR));
|
|
|
|
|
memset(&new, 0, sizeof(CSR));
|
|
|
|
|
current = &new;
|
|
|
|
|
universal_tod = 0;
|
|
|
|
|
hw_steering = 0.0;
|
|
|
|
|
hw_episode = 0;
|
|
|
|
|
hw_offset = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
SR_READ_HDR(file, key, len);
|
|
|
|
|
switch (key) {
|
|
|
|
|
case SR_SYS_CLOCK_CURRENT_CSR:
|
|
|
|
|
SR_READ_VALUE(file, len, &i, sizeof(i));
|
|
|
|
|
current = i ? &new : &old;
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_UNIVERSAL_TOD:
|
|
|
|
|
SR_READ_VALUE(file, len, &universal_tod, sizeof(universal_tod));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_HW_STEERING:
|
|
|
|
|
SR_READ_STRING(file, buf, len);
|
|
|
|
|
sscanf(buf, "%f",&f);
|
|
|
|
|
hw_steering = f;
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_HW_EPISODE:
|
|
|
|
|
SR_READ_VALUE(file, len, &hw_episode, sizeof(hw_episode));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_HW_OFFSET:
|
|
|
|
|
SR_READ_VALUE(file, len, &hw_offset, sizeof(hw_offset));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_OLD_CSR_START_TIME:
|
|
|
|
|
SR_READ_VALUE(file, len, &old.start_time, sizeof(old.start_time));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_OLD_CSR_BASE_OFFSET:
|
|
|
|
|
SR_READ_VALUE(file, len, &old.base_offset, sizeof(old.base_offset));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_OLD_CSR_FINE_S_RATE:
|
|
|
|
|
SR_READ_VALUE(file, len, &old.fine_s_rate, sizeof(old.fine_s_rate));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_OLD_CSR_GROSS_S_RATE:
|
|
|
|
|
SR_READ_VALUE(file, len, &old.gross_s_rate, sizeof(old.gross_s_rate));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_NEW_CSR_START_TIME:
|
|
|
|
|
SR_READ_VALUE(file, len, &new.start_time, sizeof(new.start_time));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_NEW_CSR_BASE_OFFSET:
|
|
|
|
|
SR_READ_VALUE(file, len, &new.base_offset, sizeof(new.base_offset));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_NEW_CSR_FINE_S_RATE:
|
|
|
|
|
SR_READ_VALUE(file, len, &new.fine_s_rate, sizeof(new.fine_s_rate));
|
|
|
|
|
break;
|
|
|
|
|
case SR_SYS_CLOCK_NEW_CSR_GROSS_S_RATE:
|
|
|
|
|
SR_READ_VALUE(file, len, &new.gross_s_rate, sizeof(new.gross_s_rate));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
SR_READ_SKIP(file, len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while ((key & SR_SYS_MASK) == SR_SYS_CLOCK);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-04 11:38:43 +00:00
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-01-07 16:12:14 +00:00
|
|
|
|
2006-01-08 13:07:32 +00:00
|
|
|
#if defined(FEATURE_INTERVAL_TIMER)
|
2006-10-02 17:14:43 +00:00
|
|
|
static void ARCH_DEP(_store_int_timer_2) (REGS *regs,int getlock)
|
2006-01-07 16:12:14 +00:00
|
|
|
{
|
2006-01-10 20:34:16 +00:00
|
|
|
S32 itimer;
|
2006-10-03 03:09:33 +00:00
|
|
|
S32 vtimer=0;
|
|
|
|
|
|
2006-01-10 20:34:16 +00:00
|
|
|
FETCH_FW(itimer, regs->psa->inttimer);
|
2006-10-02 17:14:43 +00:00
|
|
|
if(getlock)
|
|
|
|
|
{
|
|
|
|
|
OBTAIN_INTLOCK(regs->hostregs?regs:NULL);
|
|
|
|
|
}
|
2006-01-10 20:34:16 +00:00
|
|
|
if(itimer != regs->old_timer)
|
2006-01-17 19:02:50 +00:00
|
|
|
{
|
2006-10-03 03:09:33 +00:00
|
|
|
// ZZ logmsg(D_("Interval timer out of sync, core=%8.8X, internal=%8.8X\n"), itimer, regs->old_timer);
|
2006-01-13 11:00:39 +00:00
|
|
|
set_int_timer(regs, itimer);
|
2006-01-17 19:02:50 +00:00
|
|
|
}
|
2006-01-14 15:52:22 +00:00
|
|
|
else
|
2006-10-02 17:14:43 +00:00
|
|
|
{
|
|
|
|
|
itimer=int_timer(regs);
|
|
|
|
|
}
|
2006-01-14 15:52:22 +00:00
|
|
|
STORE_FW(regs->psa->inttimer, itimer);
|
2006-01-13 11:00:39 +00:00
|
|
|
#if defined(FEATURE_ECPSVM)
|
2006-01-10 20:34:16 +00:00
|
|
|
if(regs->ecps_vtmrpt)
|
|
|
|
|
{
|
2006-10-03 03:09:33 +00:00
|
|
|
FETCH_FW(vtimer, regs->ecps_vtmrpt);
|
|
|
|
|
if(vtimer != regs->ecps_oldtmr)
|
2006-01-17 19:02:50 +00:00
|
|
|
{
|
|
|
|
|
// ZZ logmsg(D_("ECPS vtimer out of sync, core=%8.8X, internal=%8.8X\n"), itimer, regs->ecps_vtimer);
|
2006-01-13 11:00:39 +00:00
|
|
|
set_ecps_vtimer(regs, itimer);
|
2006-01-17 19:02:50 +00:00
|
|
|
}
|
2006-01-14 15:52:22 +00:00
|
|
|
else
|
2006-10-02 17:14:43 +00:00
|
|
|
{
|
2006-10-03 03:09:33 +00:00
|
|
|
vtimer=ecps_vtimer(regs);
|
2006-10-02 17:14:43 +00:00
|
|
|
}
|
2006-01-14 15:52:22 +00:00
|
|
|
STORE_FW(regs->ecps_vtmrpt, itimer);
|
2006-01-10 20:34:16 +00:00
|
|
|
}
|
2006-10-03 03:09:33 +00:00
|
|
|
#endif /*defined(FEATURE_ECPSVM)*/
|
|
|
|
|
|
|
|
|
|
/* ISW : Invoke chk_int_timer *before* setting old_timer */
|
|
|
|
|
/* however, the value must be one fetched *before* */
|
|
|
|
|
/* chk_int_timer was invoked otherwise a window */
|
|
|
|
|
/* exists during which the interval timer could go */
|
|
|
|
|
/* negative undetected */
|
|
|
|
|
|
|
|
|
|
chk_int_timer(regs);
|
|
|
|
|
regs->old_timer = itimer;
|
|
|
|
|
#if defined(FEATURE_ECPSVM)
|
|
|
|
|
if(regs->ecps_vtmrpt)
|
|
|
|
|
{
|
|
|
|
|
regs->ecps_oldtmr = vtimer;
|
|
|
|
|
}
|
|
|
|
|
#endif /*defined(FEATURE_ECPSVM)*/
|
|
|
|
|
|
2006-10-02 17:14:43 +00:00
|
|
|
if(getlock)
|
|
|
|
|
{
|
|
|
|
|
RELEASE_INTLOCK(regs->hostregs?regs:NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-12-03 16:32:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
DLL_EXPORT void ARCH_DEP(store_int_timer) (REGS *regs)
|
2006-10-02 17:14:43 +00:00
|
|
|
{
|
|
|
|
|
ARCH_DEP(_store_int_timer_2) (regs,1);
|
|
|
|
|
}
|
2006-12-03 16:32:53 +00:00
|
|
|
|
|
|
|
|
|
2006-10-02 17:14:43 +00:00
|
|
|
void ARCH_DEP(store_int_timer_nolock) (REGS *regs)
|
|
|
|
|
{
|
|
|
|
|
ARCH_DEP(_store_int_timer_2) (regs,0);
|
2006-01-07 16:12:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-03 16:32:53 +00:00
|
|
|
DLL_EXPORT void ARCH_DEP(fetch_int_timer) (REGS *regs)
|
2006-01-07 16:12:14 +00:00
|
|
|
{
|
|
|
|
|
S32 itimer;
|
|
|
|
|
FETCH_FW(itimer, regs->psa->inttimer);
|
2006-10-02 17:14:43 +00:00
|
|
|
OBTAIN_INTLOCK(regs->hostregs?regs:NULL);
|
2006-01-07 16:12:14 +00:00
|
|
|
set_int_timer(regs, itimer);
|
2006-01-13 11:00:39 +00:00
|
|
|
#if defined(FEATURE_ECPSVM)
|
2006-01-10 20:34:16 +00:00
|
|
|
if(regs->ecps_vtmrpt)
|
|
|
|
|
{
|
2006-01-13 11:00:39 +00:00
|
|
|
FETCH_FW(itimer, regs->ecps_vtmrpt);
|
|
|
|
|
set_ecps_vtimer(regs, itimer);
|
2006-01-10 20:34:16 +00:00
|
|
|
}
|
|
|
|
|
#endif /*defined(FEATURE_ECPSVM)*/
|
2006-10-02 17:14:43 +00:00
|
|
|
RELEASE_INTLOCK(regs->hostregs?regs:NULL);
|
2006-01-07 16:12:14 +00:00
|
|
|
}
|
|
|
|
|
#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);
|
2006-01-01 21:37:32 +00:00
|
|
|
STORE_DW(qto.todoff, (hw_clock_l() - universal_tod) << 8);
|
2005-12-29 13:43:42 +00:00
|
|
|
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)*/
|