/*
 * nasd_timer.h
 *
 * Timing macros
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#ifndef _NASD_TIMER_H_
#define _NASD_TIMER_H_

#include <nasd/nasd_options.h>
#include <nasd/nasd_general.h>

#define NASD_TM_INTERVALS 60

#if defined(SOLARIS) && !defined(i386) && !defined(__x86_64__)
#include <nasd/generic/nasd_generic_timer.h>
#define NASD_TIMERS_DEFINED
#endif /* SOLARIS && !i386 */

#ifdef IRIX
extern nasd_uint64 nasd_rpcc_64(void);
extern nasd_uint32 nasd_rpcc_32(void);
/*
 * XXX
 *
 * Someone should change this to use cycle-counter
 * style timers. I have some code that does this
 * for 64-bit machines in nasd_irix_basic.c, though
 * I haven't tested it. The problem: how to figure
 * out if we're a 64 or 32 bit machine, and how to
 * do the right thing? Maybe two versions of all
 * the functions, with wrapper macros conditioned
 * on the size of the timer (sgisys()).
 */
#include <nasd/generic/nasd_generic_timer.h>
#define NASD_TIMERS_DEFINED
#endif /* IRIX */

/*
 * XXX clean up this mess
 */
#ifndef NASD_TIMERS_DEFINED
/*
 * Delay counters
 */
#if defined(KERNEL)

#if defined(DEC_OSF)

#include <machine/rpb.h>

typedef struct nasd_delaycounter_s nasd_delaycounter_t;
#define NASD_DUX_TIMER_SPIN_THRESH 50 /* microseconds */
#define NASD_DUX_TIMER_LOW_THRESH   1 /* microseconds */

struct nasd_delaycounter_s {
  nasd_uint32   t0;
};

#define NASD_BEGIN_DELAYCNT(_d_) { \
  (_d_)->t0 = NASD_RPCC(); \
}

#define NASD_DELAY_FROM(_d_,_us_) { \
  extern void nasd_dux_delaycnt_kicker(); \
  extern int hz; \
  \
  unsigned long _ticks, _lelticks, _lelus; \
  nasd_uint32 _t0, _t1, _elus, _elticks; \
  int _wus, _whz; \
  _ticks = rpb->rpb_counter; \
  _t0 = (_d_)->t0; \
  _t1 = NASD_RPCC(); \
  _elticks = _t1 - _t0; \
  _lelticks = (unsigned long)_elticks; \
  _lelus = _lelticks * 1000000; \
  _lelus /= _ticks; \
  _elus = _lelus; \
  _wus = (_us_) - _elus; \
  while(_wus >= NASD_DUX_TIMER_LOW_THRESH) { \
    if (_wus <= NASD_DUX_TIMER_SPIN_THRESH) { \
      microdelay(_wus); \
      break; \
    } \
    else { \
      _whz = (hz*_wus) / 1000000; \
      if (_whz) { \
        assert_wait((vm_offset_t)(_d_), FALSE); \
        timeout(nasd_dux_delaycnt_kicker, (caddr_t)(_d_), _whz); \
        thread_block(); \
      } \
      else { \
        /* just keep yieldin'... */ \
        thread_block(); \
      } \
    } \
    _t1 = NASD_RPCC(); \
    _elticks = _t1 - _t0; \
    _lelticks = (unsigned long)_elticks; \
    _lelus = _lelticks * 1000000; \
    _lelus /= _ticks; \
    _elus = _lelus; \
    _wus = (_us_) - _elus; \
  } \
}

#endif /* DEC_OSF */

#if defined(i386) || defined(__x86_64__)

#include <asm/delay.h>

#define nasd_fast_gettime(_t_)   do_gettimeofday(_t_)

typedef struct nasd_delaycounter_s nasd_delaycounter_t;

struct nasd_delaycounter_s {
  struct timeval  tv0;
};

#define NASD_BEGIN_DELAYCNT(_d_) { \
  nasd_fast_gettime(&((_d_)->tv0)); \
}

#define NASD_DELAY_FROM(_d_,_us_) { \
  struct wait_queue *_q = NULL; \
  struct timeval _tv, _tv0; \
  struct timespec _ts; \
  nasd_int64 _elus, _wus; \
  init_waitqueue(&_q); \
  _tv0 = (_d_)->tv0; \
  nasd_fast_gettime(&_tv); \
  if (_tv.tv_usec >= _tv0.tv_usec) { \
    _tv.tv_usec -= _tv0.tv_usec; \
    _tv.tv_sec -= _tv0.tv_sec; \
  } \
  else { \
    _tv.tv_usec += 1000000; \
    _tv.tv_usec -= _tv0.tv_usec; \
    _tv.tv_sec -= _tv0.tv_sec + 1; \
  } \
  _elus = _tv.tv_sec * 1000; \
  _elus += _tv.tv_usec; \
  _wus = (_us_) - _elus; \
  if (_wus > 0) { \
  /* wait for _wus microseconds */ \
    _ts.tv_sec = (_wus * 1000) / 1000000000; \
    _ts.tv_nsec = (_wus * 1000) % 1000000000; \
    interruptible_sleep_on_timeout(&_q, timespec_to_jiffies(&_ts)); \
  } \
}

#endif /* i386 */

#else /* !KERNEL */

#include <sys/time.h>

#if (NASD_CMU_PDL > 0) && defined(DEC_OSF)
#include <pdllib.h>
#define nasd_fast_gettime(_t_)	PDL_mmclock_gettime(_t_)
#endif /* (NASD_CMU_PDL > 0) && DEC_OSF */

#if defined(LINUX) && defined(KERNEL)
#define nasd_fast_gettime(_t_)   do_gettimeofday(_t_)
#endif /* defined(LINUX) && defined(KERNEL) */

#ifndef nasd_fast_gettime
#define nasd_fast_gettime(_t_)	gettimeofday(_t_, NULL)
#endif /* !nasd_fast_gettime */

typedef struct nasd_delaycounter_s nasd_delaycounter_t;

struct nasd_delaycounter_s {
  struct timeval  tv0;
};

#define NASD_BEGIN_DELAYCNT(_d_) { \
  int ret; \
  ret = nasd_fast_gettime(&((_d_)->tv0)); \
  if (ret) { \
    NASD_PANIC(); \
  } \
}

#define NASD_DELAY_FROM(_d_,_us_) { \
  struct timeval _tv, _tv0; \
  nasd_int64 _elus, _wus; \
  int ret; \
  _tv0 = (_d_)->tv0; \
  ret = nasd_fast_gettime(&_tv); \
  if (ret) { \
    NASD_PANIC(); \
  } \
  if (_tv.tv_usec >= _tv0.tv_usec) { \
    _tv.tv_usec -= _tv0.tv_usec; \
    _tv.tv_sec -= _tv0.tv_sec; \
  } \
  else { \
    _tv.tv_usec += 1000000; \
    _tv.tv_usec -= _tv0.tv_usec; \
    _tv.tv_sec -= _tv0.tv_sec; \
  } \
  /* _tv is now elapsed time */ \
  _elus = _tv.tv_sec * 1000; \
  _elus += _tv.tv_usec; \
  _wus = (_us_) - _elus; \
  /* wait for _wus microseconds */ \
  _tv.tv_sec = _wus / 1000000; \
  _tv.tv_usec = _wus % 1000000; \
  select(1, NULL, NULL, NULL, &_tv); \
}

#endif /* KERNEL */

/*
 * Performance counters
 */
#if defined(DEC_OSF) || defined(i386) || defined(__x86_64__)
typedef struct nasd_timer_s           nasd_timer_t;
typedef struct nasd_multitimer_s      nasd_multitimer_t;
typedef struct nasd_timer_interval_s  nasd_timer_interval_t;

typedef struct nasd_cycle_counter_s {
  unsigned int cc_sp;
  unsigned int cc_ep;
} nasd_cycle_counter_t;

#if defined(DEC_OSF) && defined(KERNEL)

#include <machine/rpb.h>
extern unsigned int rpcc();
#define NASD_RPCC() rpcc()
#define NASD_TICKS rpb->rpb_counter

#else /* DEC_OSF && KERNEL */

extern nasd_uint64 nasd_ctimer_ticks;
extern nasd_uint32 NASD_rpcc();
#define NASD_RPCC() NASD_rpcc()
#define NASD_TICKS nasd_ctimer_ticks

#if defined(i386) || defined(__x86_64__)
extern void NASD_rpcc_int64(nasd_uint32 *high, nasd_uint32 *low);
#endif /* i386 */

#endif /* DEC_OSF && KERNEL */

struct nasd_timer_interval_s {
  nasd_uint32   start;
  nasd_uint32   stop;
  char         *start_file;
  char         *stop_file;
  int           start_line;
  int           stop_line;
};

struct nasd_multitimer_s {
  nasd_timer_interval_t  intervals[NASD_TM_INTERVALS];
  int                    cur;
};

struct nasd_timer_s {
  nasd_timer_interval_t  intervals[1];
  int                    cur;
};

#define NASD_TM_START(_tm_) { \
  nasd_uint32 _t; \
  (_tm_)->cur = 0; \
  (_tm_)->intervals[(_tm_)->cur].start_file = __FILE__; \
  (_tm_)->intervals[(_tm_)->cur].start_line = __LINE__; \
  _t = NASD_RPCC(); \
  (_tm_)->intervals[(_tm_)->cur].start = _t; \
}

#define NASD_TM_STOP(_tm_) { \
  nasd_uint32 _t; \
  _t = NASD_RPCC(); \
  (_tm_)->intervals[(_tm_)->cur].stop = _t; \
  (_tm_)->intervals[(_tm_)->cur].stop_file = __FILE__; \
  (_tm_)->intervals[(_tm_)->cur].stop_line = __LINE__; \
}

#define NASD_TM_CKPT(_tm_) { \
  nasd_uint32 _t; \
  if (sizeof((_tm_)->intervals) == sizeof(nasd_timer_interval_t)) { \
    /* this is not a multitimer */ \
    NASD_PANIC(); \
  } \
  _t = NASD_RPCC(); \
  (_tm_)->intervals[(_tm_)->cur].stop = _t; \
  (_tm_)->intervals[(_tm_)->cur].stop_file = __FILE__; \
  (_tm_)->intervals[(_tm_)->cur].stop_line = __LINE__; \
  (_tm_)->cur++; \
  NASD_ASSERT((_tm_)->cur < NASD_TM_INTERVALS); \
  (_tm_)->intervals[(_tm_)->cur].start = _t; \
  (_tm_)->intervals[(_tm_)->cur].start_file = __FILE__; \
  (_tm_)->intervals[(_tm_)->cur].start_line = __LINE__; \
}

#define NASD_TM_REPORT(_tm_) { \
  nasd_timer_interval_t *_intv, *_intv2; \
  nasd_uint32 _diff, _secs, _nsecs, _d; \
  nasd_uint64 _ticks; \
  int _i; \
  _ticks = NASD_TICKS; \
  nasd_printf("-----\n"); \
  if (sizeof((_tm_)->intervals) > sizeof(nasd_timer_interval_t)) { \
    for(_i=0;_i<=(_tm_)->cur;_i++) { \
      _intv = &(_tm_)->intervals[_i]; \
      _diff = _intv->stop - _intv->start; \
      _secs = _diff / _ticks; \
      _nsecs = ((_diff % _ticks) * 1000000000) / _ticks; \
      nasd_printf("%u:%09u (%" NASD_64u_FMT " ticks)", _secs, _nsecs, _diff); \
      nasd_printf(" start %s:%d", _intv->start_file, _intv->start_line); \
      nasd_printf(" end %s:%d", _intv->stop_file, _intv->stop_line); \
      nasd_printf(" (%u..%u)\n", _intv->start, _intv->stop); \
    } \
  } \
  _intv = &(_tm_)->intervals[0]; \
  _intv2 = &(_tm_)->intervals[(_tm_)->cur]; \
  _diff = _intv2->stop - _intv->start; \
  _secs = _diff / _ticks; \
  _nsecs = ((_diff % _ticks) * 1000000000) / _ticks; \
  nasd_printf("%u:%09u (%" NASD_64u_FMT " ticks)", _secs, _nsecs, _diff); \
  nasd_printf(" start %s:%d", _intv->start_file, _intv->start_line); \
  nasd_printf(" end %s:%d", _intv2->stop_file, _intv2->stop_line); \
  nasd_printf(" (%u..%u)\n", _intv->start, _intv2->stop); \
  nasd_printf("-----\n"); \
}

#define NASD_TM_ELAPSED_NSEC(_tm_,_nsecp_) { \
  nasd_timer_interval_t *_intv, *_intv2; \
  nasd_uint32 _diff, _secs, _nsecs; \
  nasd_uint64 _ticks; \
  _ticks = NASD_TICKS; \
  _intv = &(_tm_)->intervals[0]; \
  _intv2 = &(_tm_)->intervals[(_tm_)->cur]; \
  _diff = _intv2->stop - _intv->start; \
  _secs = _diff / _ticks; \
  _nsecs = ((_diff % _ticks) * 1000000000) / _ticks; \
  _nsecs += (_secs * 1000000000); \
  *(_nsecp_) = _nsecs; \
}

#define NASD_TM_ELAPSED_TS(_tm_,_tsp_) { \
  nasd_timer_interval_t *_intv, *_intv2; \
  nasd_uint32 _diff, _secs, _nsecs; \
  nasd_uint64 _ticks; \
  _ticks = NASD_TICKS; \
  _intv = &(_tm_)->intervals[0]; \
  _intv2 = &(_tm_)->intervals[(_tm_)->cur]; \
  _diff = _intv2->stop - _intv->start; \
  _secs = _diff / _ticks; \
  _nsecs = ((_diff % _ticks) * 1000000000) / _ticks; \
  (_tsp_)->ts_sec = _secs; \
  (_tsp_)->ts_nsec = _nsecs; \
}

#define NASD_TM_STOP_ACCUM_TS(_tm_,_tsp_) { \
  nasd_timespec_t _tmp; \
  NASD_TM_STOP(_tm_); \
  NASD_TM_ELAPSED_TS(_tm_,&_tmp); \
  NASD_TIMESPEC_ADD(*(_tsp_), _tmp); \
}

#define NASD_TM_STOP_ELAP_TS(_tm_,_tsp_) { \
  NASD_TM_STOP(_tm_); \
  NASD_TM_ELAPSED_TS(_tm_,_tsp_); \
}

#endif /* DEC_OSF || i386 */
#endif /* !NASD_TIMERS_DEFINED */

#endif /* !_NASD_TIMER_H_ */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
