mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-10 06:03:45 +02:00
1465 lines
47 KiB
C
1465 lines
47 KiB
C
/* CLOCK.C (C) Copyright Jan Jaeger, 2000-2012 */
|
|
/* (C) and others 2013-2021 */
|
|
/* TOD Clock functions */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* The emulated hardware clock is based on the host clock, adjusted */
|
|
/* by means of an offset and a steering rate. */
|
|
|
|
/* --------------------------------------------------------------------
|
|
z/Architecture Clock Formats
|
|
--------------------------------------------------------------------
|
|
|
|
|
|
|
|
TOD (SCK, SCKC, STCK, STCKC)
|
|
+--------------------------+------+
|
|
| | |
|
|
+--------------------------+------+
|
|
0 51 63
|
|
|
|
|
|
ETOD (STCKE)
|
|
+--+---------------------------+--+------------------------+-----+
|
|
|EI| | | | PGF |
|
|
+--+---------------------------+--+------------------------+-----+
|
|
0 7 59 63 111 127
|
|
|
|
|
|
Where:
|
|
|
|
- TOD: bit 51 represents one microsecond
|
|
- TOD: bit 63 represents 244 picoseconds
|
|
|
|
- ETOD: EI = 8-bit Epoch Index field
|
|
- ETOD: PF = 16-bit Programmable Field (STKPF)
|
|
- ETOD: bit 59 represents one microsecond
|
|
- ETOD: bit 63 represents 62.5 nanoseconds
|
|
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
Hercules Clock and Timer Formats
|
|
--------------------------------------------------------------------
|
|
|
|
|
|
|
|
64-bit Clock/Timer Format
|
|
+------------------------------+--+
|
|
| | |
|
|
+------------------------------+--+
|
|
0 59 63
|
|
|
|
|
|
128-bit Clock Format
|
|
+------------------------------+--+------------------------------+
|
|
| | | |
|
|
+------------------------------+--+------------------------------+
|
|
0 59 63 127
|
|
|
|
|
|
Where:
|
|
|
|
- Bit 59 represents one microsecond
|
|
- Bit 63 represents 62.5 nanoseconds
|
|
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
Usage Notes
|
|
--------------------------------------------------------------------
|
|
|
|
- Bits 0-63 of the Hercules 64-bit clock format are identical to
|
|
bits 0-63 of the 128-bit Hercules clock format.
|
|
|
|
- The 128-bit Hercules clock format extends the 64-bit clock format
|
|
by an additional 64-bits to the right (low-order) of the 64-bit
|
|
Hercules clock format.
|
|
|
|
- Hercules timers only use the 64-bit Hercules clock/timer format.
|
|
|
|
- The Hercules clock format has a period of over 36,533 years.
|
|
|
|
- With masking, the Hercules 128-bit clock format may be used for
|
|
extended TOD clock operations.
|
|
|
|
- The ETOD2TOD() call may be used to convert a Hercules 128-bit
|
|
clock value to a standard z/Architecture 64-bit TOD clock value.
|
|
*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
DISABLE_GCC_UNUSED_FUNCTION_WARNING;
|
|
|
|
#define _CLOCK_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "inline.h"
|
|
#include "sr.h"
|
|
|
|
#ifndef COMPILE_THIS_ONLY_ONCE
|
|
#define COMPILE_THIS_ONLY_ONCE
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Global clock variables */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
ETOD universal_tod;
|
|
|
|
ETOD hw_tod; /* Hardware clock */
|
|
|
|
S64 tod_epoch; /* Bits 0-7 TOD clock epoch */
|
|
/* Bits 8-63 offset bits 0-55 */
|
|
|
|
ETOD tod_value; /* Bits 0-7 TOD clock epoch */
|
|
/* Bits 8-63 TOD bits 0-55 */
|
|
/* Bits 64-111 TOD bits 56-103 */
|
|
CSR episode_old;
|
|
CSR episode_new;
|
|
CSR* episode_current = &episode_new;
|
|
|
|
/* The hercules hardware clock, based on the universal clock, but */
|
|
/* running at its own speed as optionally set by set_tod_steering() */
|
|
/* The hardware clock returns a unique value */
|
|
|
|
double hw_steering = 0.0; /* Current TOD clock steering rate */
|
|
TOD hw_episode; /* TOD of start of steering episode */
|
|
S64 hw_offset = 0; /* Current offset between TOD - HW */
|
|
ETOD hw_unique_clock_tick = {0, 0};
|
|
|
|
int default_epoch = 1900;
|
|
int default_yroffset = 0;
|
|
int default_tzoffset = 0;
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function forward references */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static TOD universal_clock();
|
|
static TOD hw_calculate_unique_tick();
|
|
TOD hw_clock();
|
|
static TOD hw_adjust( TOD base_tod );
|
|
static TOD hw_clock_locked();
|
|
|
|
void set_tod_clock( const U64 tod );
|
|
TOD get_tod_clock( REGS* regs );
|
|
TOD update_tod_clock();
|
|
DLL_EXPORT TOD etod_clock( REGS*, ETOD*, ETOD_format );
|
|
ETOD* host_ETOD( ETOD* );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void csr_reset();
|
|
double get_tod_steering();
|
|
void set_tod_steering( const double steering );
|
|
|
|
static void set_gross_steering_rate( const S32 gsr );
|
|
static void set_fine_steering_rate ( const S32 fsr );
|
|
|
|
static INLINE void prepare_new_episode();
|
|
static INLINE void start_new_episode();
|
|
|
|
static void set_tod_offset ( const S64 offset );
|
|
static void adjust_tod_offset( const S64 offset );
|
|
|
|
S64 get_tod_epoch();
|
|
void set_tod_epoch ( const S64 );
|
|
void adjust_tod_epoch ( const S64 );
|
|
static U64 set_tod_epoch_all( const U64 epoch );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
S64 get_cpu_timer( REGS* regs );
|
|
void set_cpu_timer( REGS* regs, const S64 timer );
|
|
void update_cpu_timer();
|
|
U64 thread_cputime_us( const REGS* regs );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE void configure_time();
|
|
int configure_epoch( int epoch );
|
|
int configure_yroffset( int yroffset );
|
|
int configure_tzoffset( int tzoffset );
|
|
int query_tzoffset();
|
|
|
|
static INLINE bool is_leapyear( const unsigned int year );
|
|
static INLINE S64 lyear_adjust( const int epoch );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( _FEATURE_INTERVAL_TIMER )
|
|
#if defined( _FEATURE_ECPSVM )
|
|
|
|
static INLINE S32 get_ecps_vtimer( const REGS* regs );
|
|
static INLINE void set_ecps_vtimer( REGS* regs, const S32 vtimer );
|
|
|
|
#endif
|
|
|
|
static INLINE S32 get_int_timer( const REGS* regs );
|
|
void set_int_timer( REGS* regs, const S32 itimer );
|
|
int chk_int_timer( REGS* regs );
|
|
|
|
#endif // defined( _FEATURE_INTERVAL_TIMER )
|
|
#endif // COMPILE_THIS_ONLY_ONCE
|
|
|
|
//-------------------------------------------------------------------
|
|
// ARCH_DEP() code
|
|
//-------------------------------------------------------------------
|
|
// ARCH_DEP (build-architecture / FEATURE-dependent) functions here.
|
|
// All BUILD architecture dependent (ARCH_DEP) function are compiled
|
|
// multiple times (once for each defined build architecture) and each
|
|
// time they are compiled with a different set of FEATURE_XXX defines
|
|
// appropriate for that architecture. Use #ifdef FEATURE_XXX guards
|
|
// to check whether the current BUILD architecture has that given
|
|
// feature #defined for it or not. WARNING: Do NOT use _FEATURE_XXX.
|
|
// The underscore feature #defines mean something else entirely. Only
|
|
// test for FEATURE_XXX. (WITHOUT the underscore)
|
|
//-------------------------------------------------------------------
|
|
|
|
#if defined( FEATURE_028_TOD_CLOCK_STEER_FACILITY )
|
|
|
|
void ARCH_DEP(set_gross_s_rate) (REGS *regs)
|
|
{
|
|
S32 gsr;
|
|
gsr = ARCH_DEP(vfetch4) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
|
|
set_gross_steering_rate(gsr);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(set_fine_s_rate) (REGS *regs)
|
|
{
|
|
S32 fsr;
|
|
fsr = ARCH_DEP(vfetch4) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
|
|
set_fine_steering_rate(fsr);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(set_tod_offset) (REGS *regs)
|
|
{
|
|
S64 offset;
|
|
offset = ARCH_DEP(vfetch8) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
|
|
set_tod_offset(offset >> 8);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP( set_tod_offset_user )( REGS* regs )
|
|
{
|
|
S64 offset;
|
|
|
|
/* "This function specifies a value that is to replace the
|
|
*USER*-specified portion of the TOD epoch difference;"
|
|
|
|
Since, at the basic-machine or LPAR hypervisor level, the
|
|
user-specified epoch difference is always ZERO (see the
|
|
'query_tod_offset_user' function), we should ideally never
|
|
see this function ever being called since z/VM should be
|
|
handling it itself (i.e. simulating it for the SIE guest).
|
|
Therefore we ignore this call and do absolutely nothing.
|
|
*/
|
|
|
|
/* Fetch the value anyway to check for any addressing exception */
|
|
offset = ARCH_DEP( vfetch8 )( regs->GR(1) & ADDRESS_MAXWRAP( regs ), 1, regs );
|
|
|
|
/* Inform the user that an unexpected situation has occurred */
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
char buf[32];
|
|
MSGBUF( buf, "PTFF-STOU 0x%16.16"PRIX64"!", offset );
|
|
WRMSG( HHC90000, "D", buf );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
|
|
void ARCH_DEP( set_tod_offset_extended )( REGS* regs )
|
|
{
|
|
// TODO...
|
|
}
|
|
|
|
#endif /* defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
|
|
void ARCH_DEP( set_tod_offset_user_extended )( REGS* regs )
|
|
{
|
|
// TODO...
|
|
}
|
|
|
|
#endif /* defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(adjust_tod_offset) (REGS *regs)
|
|
{
|
|
S64 offset;
|
|
offset = ARCH_DEP(vfetch8) (regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
|
|
adjust_tod_offset(offset >> 8);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(query_physical_clock) (REGS *regs)
|
|
{
|
|
ARCH_DEP(vstore8) (universal_clock() << 8, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP( query_utc_information )( REGS* regs )
|
|
{
|
|
/* "The information in the UIB is provided by the server-time-
|
|
protocol (STP) facility that also controls TOD-clock steering.
|
|
When STP is not installed, all fields in the UIB are zero."
|
|
*/
|
|
static const BYTE uib[256] = {0};
|
|
ARCH_DEP( vstorec )( &uib, sizeof( uib )-1, regs->GR(1) & ADDRESS_MAXWRAP( regs ), 1, regs );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(query_steering_information) (REGS *regs)
|
|
{
|
|
PTFFQSI qsi;
|
|
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
STORE_DW( qsi.physclk, universal_clock() << 8);
|
|
|
|
STORE_DW( qsi.oldestart, episode_old.start_time << 8);
|
|
STORE_DW( qsi.oldebase, episode_old.base_offset << 8);
|
|
STORE_FW( qsi.oldfsr, episode_old.fine_s_rate );
|
|
STORE_FW( qsi.oldgsr, episode_old.gross_s_rate );
|
|
|
|
STORE_DW( qsi.newestart, episode_new.start_time << 8);
|
|
STORE_DW( qsi.newebase, episode_new.base_offset << 8);
|
|
STORE_FW( qsi.newfsr, episode_new.fine_s_rate );
|
|
STORE_FW( qsi.newgsr, episode_new.gross_s_rate );
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
ARCH_DEP(vstorec) (&qsi, sizeof(qsi)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
|
|
void ARCH_DEP( query_steering_information_extended )( REGS* regs )
|
|
{
|
|
// TODO...
|
|
}
|
|
|
|
#endif /* defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Helper function to fill in PTFFQTO struct fields */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static void build_qto_locked( PTFFQTO* qto, REGS* regs )
|
|
{
|
|
STORE_DW( qto->todoff, (hw_clock_locked() - universal_tod.high) << 8);
|
|
STORE_DW( qto->physclk, (universal_tod.high << 8) | (universal_tod.low >> (64-8)));
|
|
STORE_DW( qto->ltodoff, episode_current->base_offset << 8);
|
|
STORE_DW( qto->todepoch, regs->tod_epoch << 8);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(query_tod_offset) (REGS *regs)
|
|
{
|
|
PTFFQTO qto;
|
|
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
build_qto_locked( &qto, regs );
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
ARCH_DEP(vstorec) (&qto, sizeof(qto)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP( query_tod_offset_user )( REGS* regs )
|
|
{
|
|
/* "The 64-bit TOD user specified epoch difference value returned
|
|
is the user-specified portion of the *GUEST* epoch difference
|
|
for the current level of CPU execution. When executed at the
|
|
basic-machine or LPAR hypervisor level, this value is zero."
|
|
*/
|
|
struct
|
|
{
|
|
PTFFQTO qto;
|
|
DBLWRD tod_user_specified_epoch_difference;
|
|
}
|
|
qtou;
|
|
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
build_qto_locked( &qtou.qto, regs );
|
|
STORE_DW( qtou.tod_user_specified_epoch_difference, 0 );
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
ARCH_DEP( vstorec )( &qtou, sizeof( qtou )-1, regs->GR(1) & ADDRESS_MAXWRAP( regs ), 1, regs );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
|
|
void ARCH_DEP( query_tod_offset_user_extended )( REGS* regs )
|
|
{
|
|
// TODO...
|
|
}
|
|
|
|
#endif /* defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void ARCH_DEP(query_available_functions) (REGS *regs)
|
|
{
|
|
BYTE qaf[16] = {0};
|
|
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QAF );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QTO );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QSI );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QPT );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QUI );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QTOU );
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QSIE );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_QTOUE );
|
|
#endif
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_ATO );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_STO );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_SFS );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_SGS );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_STOU );
|
|
#if defined( FEATURE_139_MULTIPLE_EPOCH_FACILITY )
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_STOE );
|
|
BIT_ARRAY_SET( qaf, PTFF_GPR0_FC_STOUE );
|
|
#endif
|
|
|
|
ARCH_DEP(vstorec) (&qaf, sizeof(qaf)-1, regs->GR(1) & ADDRESS_MAXWRAP(regs), 1, regs);
|
|
}
|
|
#endif /* defined( FEATURE_028_TOD_CLOCK_STEER_FACILITY ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if defined( FEATURE_INTERVAL_TIMER )
|
|
|
|
void ARCH_DEP( store_int_timer_locked )( REGS* regs )
|
|
{
|
|
S32 itimer;
|
|
S32 vtimer=0;
|
|
|
|
itimer = get_int_timer( regs );
|
|
STORE_FW( regs->psa->inttimer, itimer );
|
|
|
|
#if defined( FEATURE_ECPSVM )
|
|
|
|
if (regs->ecps_vtmrpt)
|
|
{
|
|
vtimer = get_ecps_vtimer( regs );
|
|
STORE_FW( regs->ecps_vtmrpt, vtimer );
|
|
}
|
|
#endif
|
|
|
|
chk_int_timer( regs );
|
|
|
|
#if defined( FEATURE_ECPSVM )
|
|
|
|
if (regs->ecps_vtmrpt)
|
|
regs->ecps_oldtmr = vtimer;
|
|
|
|
#endif
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT
|
|
void ARCH_DEP( store_int_timer )( REGS* regs )
|
|
{
|
|
OBTAIN_INTLOCK( HOSTREGS ? regs : NULL );
|
|
{
|
|
ARCH_DEP( store_int_timer_locked )( regs );
|
|
}
|
|
RELEASE_INTLOCK( HOSTREGS ? regs : NULL );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT
|
|
void ARCH_DEP( fetch_int_timer )( REGS* regs )
|
|
{
|
|
S32 itimer;
|
|
|
|
FETCH_FW( itimer, regs->psa->inttimer );
|
|
|
|
OBTAIN_INTLOCK( HOSTREGS ? regs : NULL );
|
|
{
|
|
set_int_timer( regs, itimer );
|
|
|
|
#if defined( FEATURE_ECPSVM )
|
|
if (regs->ecps_vtmrpt)
|
|
{
|
|
FETCH_FW( itimer, regs->ecps_vtmrpt );
|
|
set_ecps_vtimer( regs, itimer );
|
|
}
|
|
#endif
|
|
}
|
|
RELEASE_INTLOCK( HOSTREGS ? regs : NULL );
|
|
}
|
|
|
|
#endif /* defined( FEATURE_INTERVAL_TIMER ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if !defined( _GEN_ARCH )
|
|
|
|
#if defined( _ARCH_NUM_1 )
|
|
#define _GEN_ARCH _ARCH_NUM_1
|
|
#include "clock.c"
|
|
#endif
|
|
|
|
#if defined( _ARCH_NUM_2 )
|
|
#undef _GEN_ARCH
|
|
#define _GEN_ARCH _ARCH_NUM_2
|
|
#include "clock.c"
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* non-ARCH_DEP section: compiled only ONCE after last arch built */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Note: the last architecture has been built so the normal non- */
|
|
/* underscore FEATURE values are now #defined according to the */
|
|
/* LAST built architecture just built (usually zarch = 900). This */
|
|
/* means from this point onward (to the end of file) you should */
|
|
/* ONLY be testing the underscore _FEATURE values to see if the */
|
|
/* given feature was defined for *ANY* of the build architectures. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* host_ETOD - Primary high-resolution clock fetch and conversion */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
ETOD* host_ETOD( ETOD* ETOD )
|
|
{
|
|
struct timespec time;
|
|
|
|
/* Should use CLOCK_MONOTONIC + adjustment, but host sleep/hibernate
|
|
* destroys consistent monotonic clock.
|
|
*/
|
|
|
|
clock_gettime( CLOCK_REALTIME, &time );
|
|
timespec2ETOD( ETOD, &time );
|
|
return ( ETOD ); /* Return address of result */
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void csr_reset()
|
|
{
|
|
episode_new.start_time = 0;
|
|
episode_new.base_offset = 0;
|
|
episode_new.fine_s_rate = 0;
|
|
episode_new.gross_s_rate = 0;
|
|
|
|
episode_current = &episode_new;
|
|
|
|
episode_old = episode_new;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
TOD universal_clock() /* really: any clock used as a base */
|
|
{
|
|
host_ETOD( &universal_tod );
|
|
return (universal_tod.high);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
TOD hw_calculate_unique_tick()
|
|
{
|
|
static const ETOD m1 = ETOD_init(0,65536);
|
|
|
|
ETOD temp;
|
|
register TOD result;
|
|
register int n;
|
|
|
|
temp.high = universal_tod.high;
|
|
temp.low = universal_tod.low;
|
|
hw_unique_clock_tick.low = 1;
|
|
for (n = 0; n < 65536; ++n)
|
|
result = hw_adjust(universal_clock());
|
|
ETOD_sub(&temp, universal_tod, temp);
|
|
ETOD_sub(&temp, temp, m1);
|
|
ETOD_shift(&hw_unique_clock_tick, temp, 16);
|
|
if (hw_unique_clock_tick.low == 0 &&
|
|
hw_unique_clock_tick.high == 0)
|
|
hw_unique_clock_tick.high = 1;
|
|
|
|
#if defined(TOD_95BIT_PRECISION) || \
|
|
defined(TOD_64BIT_PRECISION) || \
|
|
defined(TOD_MIN_PRECISION)
|
|
|
|
else
|
|
{
|
|
#if defined(TOD_95BIT_PRECISION)
|
|
static const ETOD adj = ETOD_init(0,0x0000000100000000ULL);
|
|
#else
|
|
static const ETOD adj = ETOD_init(0,0x8000000000000000ULL);
|
|
#endif
|
|
|
|
ETOD_add(&hw_unique_clock_tick, hw_unique_clock_tick, adj);
|
|
|
|
#if defined(TOD_95BIT_PRECISION)
|
|
hw_unique_clock_tick.low &= 0xFFFFFFFE00000000ULL;
|
|
#else
|
|
hw_unique_clock_tick.low = 0;
|
|
#endif
|
|
}
|
|
|
|
#endif /* defined(TOD_95BIT_PRECISION) ... */
|
|
|
|
return ( result );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
TOD hw_adjust( TOD base_tod )
|
|
{
|
|
/* Apply hardware offset, this is the offset achieved by all
|
|
previous steering episodes */
|
|
base_tod += hw_offset;
|
|
|
|
/* Apply the steering offset from the current steering episode */
|
|
/* TODO: Shift resolution to permit adjustment by less than 62.5
|
|
* nanosecond increments (1/16 microsecond).
|
|
*/
|
|
base_tod += (S64)(base_tod - hw_episode) * hw_steering;
|
|
|
|
/* Ensure that the clock returns a unique value */
|
|
if (hw_tod.high < base_tod)
|
|
hw_tod.high = base_tod,
|
|
hw_tod.low = universal_tod.low;
|
|
else if (hw_unique_clock_tick.low == 0 &&
|
|
hw_unique_clock_tick.high == 0)
|
|
hw_calculate_unique_tick();
|
|
else
|
|
ETOD_add( &hw_tod, hw_tod, hw_unique_clock_tick );
|
|
|
|
return ( hw_tod.high );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
TOD hw_clock_locked()
|
|
{
|
|
/* Get time of day (GMT); adjust speed and ensure uniqueness */
|
|
return hw_adjust( universal_clock() );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
TOD hw_clock()
|
|
{
|
|
register TOD temp_tod;
|
|
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
/* Get time of day (GMT); adjust speed and ensure uniqueness */
|
|
temp_tod = hw_clock_locked();
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
return temp_tod;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* set_tod_steering */
|
|
/*-------------------------------------------------------------------*/
|
|
/* 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( const double steering )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
/* Get current offset between hw_adjust and universal TOD value */
|
|
hw_offset = hw_clock_locked() - universal_tod.high;
|
|
|
|
hw_episode = hw_tod.high;
|
|
hw_steering = steering;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* start_new_episode */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
void start_new_episode()
|
|
{
|
|
hw_offset = hw_tod.high - universal_tod.high;
|
|
hw_episode = hw_tod.high;
|
|
episode_new.start_time = hw_episode;
|
|
/* TODO: Convert to binary arithmetic to avoid floating point conversions */
|
|
hw_steering = ldexp(2,-44) *
|
|
(S32)(episode_new.fine_s_rate + episode_new.gross_s_rate);
|
|
episode_current = &episode_new;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* prepare_new_episode */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
void prepare_new_episode()
|
|
{
|
|
if (episode_current == &episode_new)
|
|
{
|
|
episode_old = episode_new;
|
|
episode_current = &episode_old;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Adjust the epoch for all active cpu's in the configuration */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
U64 set_tod_epoch_all( const 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 < sysblk.maxcpu; 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()
|
|
{
|
|
return hw_steering;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void set_tod_epoch( const S64 epoch )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
csr_reset();
|
|
tod_epoch = epoch;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
set_tod_epoch_all( tod_epoch );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void adjust_tod_epoch( const S64 adjustment )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
csr_reset();
|
|
tod_epoch += adjustment;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
set_tod_epoch_all( tod_epoch );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void set_tod_clock(const U64 tod)
|
|
{
|
|
set_tod_epoch(tod - hw_clock());
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
S64 get_tod_epoch()
|
|
{
|
|
return tod_epoch;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
void set_gross_steering_rate( const S32 gsr )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
prepare_new_episode();
|
|
episode_new.gross_s_rate = gsr;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
void set_fine_steering_rate( const S32 fsr )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
prepare_new_episode();
|
|
episode_new.fine_s_rate = fsr;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
void set_tod_offset( const S64 offset )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
prepare_new_episode();
|
|
episode_new.base_offset = offset;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static
|
|
void adjust_tod_offset( const S64 offset )
|
|
{
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
prepare_new_episode();
|
|
episode_new.base_offset = episode_old.base_offset + offset;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* thread_cputime_us */
|
|
/*-------------------------------------------------------------------*/
|
|
/* 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.*/
|
|
/* */
|
|
/* To be in agreement with reporting of time in association with */
|
|
/* real diagnose code x'204' for partition management and resource */
|
|
/* reporting, only "user" time is considered as CPU time used. */
|
|
/* */
|
|
/* System time is considered to be overhead time of the system */
|
|
/* (partition overhead or management time). */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
U64 thread_cputime_us( const REGS* regs )
|
|
{
|
|
U64 result;
|
|
int rc = -1;
|
|
struct timespec cputime;
|
|
|
|
if (sysblk.cpuclockid[ regs->cpuad ])
|
|
{
|
|
rc = clock_gettime( sysblk.cpuclockid[ regs->cpuad ], &cputime );
|
|
}
|
|
result = (likely(rc == 0)) ? timespec2usecs( &cputime )
|
|
: ETOD_high64_to_usecs( host_tod() );
|
|
return (result);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void set_cpu_timer( REGS* regs, const S64 timer )
|
|
{
|
|
U64 new_epoch_us = (sysblk.lparmode && !WAITSTATE( ®s->psw )) ?
|
|
thread_cputime_us( regs ) : (U64) ETOD_high64_to_usecs( host_tod() );
|
|
|
|
if (regs->bcputime <= new_epoch_us)
|
|
{
|
|
/* Update real CPU time used and base CPU time epoch */
|
|
regs->rcputime += new_epoch_us - regs->bcputime;
|
|
regs->bcputime = new_epoch_us;
|
|
}
|
|
|
|
regs->cpu_timer = TOD_high64_to_ETOD_high56( timer ) + hw_clock();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
S64 get_cpu_timer( REGS* regs )
|
|
{
|
|
S64 timer;
|
|
timer = (S64)ETOD_high64_to_TOD_high56( regs->cpu_timer - hw_clock() );
|
|
return timer;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT
|
|
TOD etod_clock( REGS* regs, ETOD* ETOD, ETOD_format format )
|
|
{
|
|
/* STORE CLOCK and STORE CLOCK EXTENDED values must be in ascending
|
|
* order for comparison. Consequently, swap delays for a subsequent
|
|
* STORE CLOCK, STORE CLOCK EXTENDED, or TRACE instruction may be
|
|
* introduced when a STORE CLOCK value is advanced due to the use of
|
|
* the CPU address in bits 66-71.
|
|
*
|
|
* If the regs pointer is null, then the request is a raw request,
|
|
* and the format operand should specify ETOD_raw or ETOD_fast. For
|
|
* raw and fast requests, the CPU address is not inserted into the
|
|
* returned value.
|
|
*
|
|
* A spin loop is used for the introduction of the delay, moderated
|
|
* by obtaining and releasing of the TOD lock. This permits raw and
|
|
* fast clock requests to complete without additional delay.
|
|
*/
|
|
|
|
U64 high;
|
|
U64 low;
|
|
U8 swapped = 0;
|
|
|
|
do
|
|
{
|
|
obtain_lock(&sysblk.todlock);
|
|
|
|
high = hw_clock_locked();
|
|
low = hw_tod.low;
|
|
|
|
/* If we are in the old episode, and the new episode has arrived
|
|
* then we must take action to start the new episode.
|
|
*/
|
|
if (episode_current == &episode_old)
|
|
start_new_episode();
|
|
|
|
/* Set the clock to the new updated value with offset applied */
|
|
high += episode_current->base_offset;
|
|
|
|
/* Place CPU stamp into clock value for Standard and Extended
|
|
* formats (raw or fast requests fall through)
|
|
*/
|
|
if (regs && format >= ETOD_standard)
|
|
{
|
|
register U64 cpuad;
|
|
register U64 amask;
|
|
register U64 lmask;
|
|
|
|
/* Set CPU address masks */
|
|
if (sysblk.maxcpu <= 64)
|
|
amask = 0x3F, lmask = 0xFFFFFFFFFFC00000ULL;
|
|
else if (sysblk.maxcpu <= 128)
|
|
amask = 0x7F, lmask = 0xFFFFFFFFFF800000ULL;
|
|
else /* sysblk.maxcpu <= 256) */
|
|
amask = 0xFF, lmask = 0xFFFFFFFFFF000000ULL;
|
|
|
|
/* Clean CPU address */
|
|
cpuad = (U64)regs->cpuad & amask;
|
|
|
|
switch (format)
|
|
{
|
|
/* Standard TOD format */
|
|
case ETOD_standard:
|
|
low &= lmask << 40;
|
|
low |= cpuad << 56;
|
|
break;
|
|
|
|
/* Extended TOD format */
|
|
case ETOD_extended:
|
|
low &= lmask;
|
|
low |= cpuad << 16;
|
|
if (low == 0)
|
|
low = (amask + 1) << 16;
|
|
low |= regs->todpr;
|
|
break;
|
|
default:
|
|
ASSERT(0); /* unexpected */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (/* New clock value > Old clock value */
|
|
high > tod_value.high ||
|
|
(high == tod_value.high &&
|
|
low > tod_value.low) ||
|
|
/* or Clock Wrap */
|
|
unlikely(unlikely((tod_value.high & 0x8000000000000000ULL) == 0x8000000000000000ULL &&
|
|
( high & 0x8000000000000000ULL) == 0)))
|
|
{
|
|
tod_value.high = high;
|
|
tod_value.low = low;
|
|
swapped = 1;
|
|
}
|
|
else if (format <= ETOD_fast)
|
|
{
|
|
high = tod_value.high;
|
|
low = tod_value.low;
|
|
swapped = 1;
|
|
}
|
|
|
|
if (swapped)
|
|
{
|
|
ETOD->high = high += regs->tod_epoch;
|
|
ETOD->low = low;
|
|
}
|
|
|
|
release_lock(&sysblk.todlock);
|
|
|
|
} while (!swapped);
|
|
|
|
return ( high );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
TOD get_tod_clock( REGS* regs )
|
|
{
|
|
ETOD ETOD;
|
|
return etod_clock( regs, &ETOD, ETOD_fast );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* is_leapyear */
|
|
/*-------------------------------------------------------------------*/
|
|
/*
|
|
* Algorithm:
|
|
*
|
|
* if year modulo 400 is 0 then is_leap_year
|
|
* else if year modulo 100 is 0 then not_leap_year
|
|
* else if year modulo 4 is 0 then is_leap_year
|
|
* else not_leap_year
|
|
*
|
|
*
|
|
* Notes and Restrictions:
|
|
*
|
|
* 1) In reality, only valid for years 1582 and later. 1582 was the
|
|
* first year of Gregorian calendar; actual years are dependent upon
|
|
* year of acceptance by any given government and/or agency. For
|
|
* example, Britain and the British empire did not adopt the
|
|
* calendar until 1752; Alaska did not adopt the calendar until
|
|
* 1867.
|
|
*
|
|
* 2) Minimum validity period for algorithm is 3,300 years after 1582
|
|
* (4882), at which point the calendar may be off by one full day.
|
|
*
|
|
* 3) Most likely invalid for years after 8000 due to unpredictability
|
|
* in the earth's long-time rotational changes.
|
|
*
|
|
* 4) For our purposes, year zero is treated as a leap year.
|
|
*
|
|
*
|
|
* References:
|
|
*
|
|
* http://scienceworld.wolfram.com/astronomy/LeapYear.html
|
|
* http://www.timeanddate.com/date/leapyear.html
|
|
* http://www.usno.navy.mil/USNO/astronomical-applications/
|
|
* astronomical-information-center/leap-years
|
|
* http://en.wikipedia.org/wiki/Leap_year
|
|
* http://en.wikipedia.org/wiki/0_(year)
|
|
* http://en.wikipedia.org/wiki/1_BC
|
|
* http://en.wikipedia.org/wiki/Proleptic_calendar
|
|
* http://en.wikipedia.org/wiki/Proleptic_Julian_calendar
|
|
* http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
|
|
* http://dotat.at/tmp/ISO_8601-2004_E.pdf
|
|
* http://tools.ietf.org/html/rfc3339
|
|
*/
|
|
static INLINE bool is_leapyear( const unsigned int year )
|
|
{
|
|
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
S64 lyear_adjust( const int epoch )
|
|
{
|
|
int year, leapyear;
|
|
TOD tod = hw_clock();
|
|
|
|
if (tod >= TOD_YEAR)
|
|
{
|
|
tod -= TOD_YEAR;
|
|
year = (tod / TOD_4YEARS * 4) + 1;
|
|
tod %= TOD_4YEARS;
|
|
|
|
if ((leapyear = tod / TOD_YEAR) == 4)
|
|
year--;
|
|
|
|
year += leapyear;
|
|
}
|
|
else
|
|
year = 0;
|
|
|
|
if (epoch > 0)
|
|
return ( ((!is_leapyear(year)) && (((year % 4) - (epoch % 4)) <= 0)) ? -TOD_DAY : 0 );
|
|
else
|
|
return ( ((is_leapyear(year) && (-epoch % 4) != 0) || ((year % 4) + (-epoch % 4) > 4)) ? TOD_DAY : 0 );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
void configure_time()
|
|
{
|
|
int epoch;
|
|
S64 ly1960;
|
|
|
|
/* Set up the system TOD clock offset: compute the number of
|
|
* microseconds offset to 0000 GMT, 1 January 1900.
|
|
*/
|
|
|
|
if( (epoch = default_epoch) == 1960 )
|
|
ly1960 = ETOD_DAY;
|
|
else
|
|
ly1960 = 0;
|
|
|
|
epoch -= 1900 + default_yroffset;
|
|
|
|
set_tod_epoch(((epoch*365+(epoch/4))*-ETOD_DAY)+lyear_adjust(epoch)+ly1960);
|
|
|
|
/* Set the timezone offset */
|
|
adjust_tod_epoch((((default_tzoffset / 100) * 60) + /* Hours -> Minutes */
|
|
(default_tzoffset % 100)) * /* Minutes */
|
|
ETOD_MIN); /* Convert to ETOD format */
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* epoch 1900|1960 */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_epoch( int epoch )
|
|
{
|
|
if (epoch != 1900 && epoch != 1960)
|
|
return -1;
|
|
|
|
default_epoch = epoch;
|
|
configure_time();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* yroffset +|-142 */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_yroffset( int yroffset )
|
|
{
|
|
if (yroffset < -142 || yroffset > 142)
|
|
return -1;
|
|
|
|
default_yroffset = yroffset;
|
|
|
|
configure_time();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* tzoffset -2359..+2359 */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_tzoffset(int tzoffset)
|
|
{
|
|
if(tzoffset < -2359 || tzoffset > 2359)
|
|
return -1;
|
|
|
|
default_tzoffset = tzoffset;
|
|
configure_time();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Query current tzoffset value for reporting */
|
|
/*-------------------------------------------------------------------*/
|
|
int query_tzoffset()
|
|
{
|
|
return default_tzoffset;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* update_tod_clock */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* This function is called by timer_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. */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
TOD update_tod_clock()
|
|
{
|
|
TOD new_clock;
|
|
|
|
obtain_lock( &sysblk.todlock );
|
|
{
|
|
new_clock = hw_clock_locked();
|
|
|
|
/* If we are in the old episode, and the new episode has arrived
|
|
then we must take action to start the new episode */
|
|
if (episode_current == &episode_old)
|
|
start_new_episode();
|
|
|
|
/* Set the clock to the new updated value with offset applied */
|
|
new_clock += episode_current->base_offset;
|
|
tod_value.high = new_clock;
|
|
tod_value.low = hw_tod.low;
|
|
}
|
|
release_lock( &sysblk.todlock );
|
|
|
|
/* Update the timers and check if either a clock related event has
|
|
become pending */
|
|
update_cpu_timer();
|
|
|
|
return new_clock;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// _FEATURE_XXX code
|
|
//-------------------------------------------------------------------
|
|
// Place any _FEATURE_XXX depdendent functions (WITH the underscore)
|
|
// here. You may need to define such functions whenever one or more
|
|
// build architectures has a given FEATURE_XXX (WITHOUT underscore)
|
|
// defined for it. The underscore means AT LEAST ONE of the build
|
|
// architectures #defined that feature. (See featchk.h) You must NOT
|
|
// use any #ifdef FEATURE_XXX here. Test for ONLY for _FEATURE_XXX.
|
|
// The functions in this area are compiled ONCE (only ONE time) and
|
|
// ONLY one time but are always compiled LAST after everything else.
|
|
//-------------------------------------------------------------------
|
|
|
|
#if defined( _FEATURE_INTERVAL_TIMER )
|
|
#if defined( _FEATURE_ECPSVM )
|
|
|
|
static INLINE
|
|
S32 get_ecps_vtimer( const REGS* regs )
|
|
{
|
|
return (S32)TOD_TO_ITIMER((S64)(regs->ecps_vtimer - hw_clock()));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
void set_ecps_vtimer( REGS* regs, const S32 vtimer )
|
|
{
|
|
regs->ecps_vtimer = (U64)(hw_clock() + ITIMER_TO_TOD(vtimer));
|
|
regs->ecps_oldtmr = vtimer;
|
|
}
|
|
|
|
#endif /* defined( _FEATURE_ECPSVM ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static INLINE
|
|
S32 get_int_timer( const REGS* regs )
|
|
{
|
|
return (S32)TOD_TO_ITIMER((S64)(regs->int_timer - hw_clock()));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
void set_int_timer( REGS* regs, const S32 itimer )
|
|
{
|
|
regs->int_timer = (U64)(hw_clock() + ITIMER_TO_TOD(itimer));
|
|
regs->old_timer = itimer;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
int chk_int_timer( REGS* regs )
|
|
{
|
|
S32 itimer;
|
|
int pending = 0;
|
|
|
|
itimer = get_int_timer( regs );
|
|
if (itimer < 0 && regs->old_timer >= 0)
|
|
{
|
|
ON_IC_ITIMER( regs );
|
|
pending = 1;
|
|
regs->old_timer=itimer;
|
|
}
|
|
#if defined( _FEATURE_ECPSVM )
|
|
|
|
if (regs->ecps_vtmrpt)
|
|
{
|
|
itimer = get_ecps_vtimer( regs );
|
|
|
|
if (itimer < 0 && regs->ecps_oldtmr >= 0)
|
|
{
|
|
ON_IC_ECPSVTIMER( regs );
|
|
pending += 2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return pending;
|
|
}
|
|
#endif /* defined( _FEATURE_INTERVAL_TIMER ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules Suspend/Resume support */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#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 = (episode_current == &episode_new);
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_CURRENT_CSR, i, sizeof(i));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_UNIVERSAL_TOD, universal_tod.high, sizeof(universal_tod.high));
|
|
MSGBUF(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, episode_old.start_time, sizeof(episode_old.start_time));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_BASE_OFFSET, episode_old.base_offset, sizeof(episode_old.base_offset));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_FINE_S_RATE, episode_old.fine_s_rate, sizeof(episode_old.fine_s_rate));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_OLD_CSR_GROSS_S_RATE, episode_old.gross_s_rate, sizeof(episode_old.gross_s_rate));
|
|
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_START_TIME, episode_new.start_time, sizeof(episode_new.start_time));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_BASE_OFFSET, episode_new.base_offset, sizeof(episode_new.base_offset));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_FINE_S_RATE, episode_new.fine_s_rate, sizeof(episode_new.fine_s_rate));
|
|
SR_WRITE_VALUE(file, SR_SYS_CLOCK_NEW_CSR_GROSS_S_RATE, episode_new.gross_s_rate, sizeof(episode_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(&episode_old, 0, sizeof(CSR));
|
|
memset(&episode_new, 0, sizeof(CSR));
|
|
episode_current = &episode_new;
|
|
universal_tod.high = universal_tod.low = 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));
|
|
episode_current = i ? &episode_new : &episode_old;
|
|
break;
|
|
case SR_SYS_CLOCK_UNIVERSAL_TOD:
|
|
SR_READ_VALUE(file, len, &universal_tod.high, sizeof(universal_tod.high));
|
|
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, &episode_old.start_time, sizeof(episode_old.start_time));
|
|
break;
|
|
case SR_SYS_CLOCK_OLD_CSR_BASE_OFFSET:
|
|
SR_READ_VALUE(file, len, &episode_old.base_offset, sizeof(episode_old.base_offset));
|
|
break;
|
|
case SR_SYS_CLOCK_OLD_CSR_FINE_S_RATE:
|
|
SR_READ_VALUE(file, len, &episode_old.fine_s_rate, sizeof(episode_old.fine_s_rate));
|
|
break;
|
|
case SR_SYS_CLOCK_OLD_CSR_GROSS_S_RATE:
|
|
SR_READ_VALUE(file, len, &episode_old.gross_s_rate, sizeof(episode_old.gross_s_rate));
|
|
break;
|
|
case SR_SYS_CLOCK_NEW_CSR_START_TIME:
|
|
SR_READ_VALUE(file, len, &episode_new.start_time, sizeof(episode_new.start_time));
|
|
break;
|
|
case SR_SYS_CLOCK_NEW_CSR_BASE_OFFSET:
|
|
SR_READ_VALUE(file, len, &episode_new.base_offset, sizeof(episode_new.base_offset));
|
|
break;
|
|
case SR_SYS_CLOCK_NEW_CSR_FINE_S_RATE:
|
|
SR_READ_VALUE(file, len, &episode_new.fine_s_rate, sizeof(episode_new.fine_s_rate));
|
|
break;
|
|
case SR_SYS_CLOCK_NEW_CSR_GROSS_S_RATE:
|
|
SR_READ_VALUE(file, len, &episode_new.gross_s_rate, sizeof(episode_new.gross_s_rate));
|
|
break;
|
|
default:
|
|
SR_READ_SKIP(file, len);
|
|
break;
|
|
}
|
|
} while ((key & SR_SYS_MASK) == SR_SYS_CLOCK);
|
|
return 0;
|
|
}
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|