/*
 * nasd_irix_basic.c
 *
 * Basic system support stuff
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_sys.h>

#ifndef KERNEL
#include <errno.h>
#include <fcntl.h>
#include <invent.h>
#include <stddef.h>
#endif /* !KERNEL */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/syssgi.h>

static nasd_timespec_t nasd_irix_last_time = {0,0};
static nasd_timespec_t nasd_irix_uniq_time = {0,0};

void
nasd_gettime(
  nasd_timespec_t  *tm)
{
#ifdef KERNEL
  struct timeval tv;

  microtime(&tv);
  tm->ts_sec = tv.tv_sec;
  tm->ts_nsec = tv.tv_usec*1000;
#else /* KERNEL */
  struct timespec ts;

  clock_gettime(CLOCK_REALTIME, &ts);
  tm->ts_sec = ts.tv_sec;
  tm->ts_nsec = ts.tv_nsec;
#endif /* KERNEL */
  if ((tm->ts_sec == nasd_irix_last_time.ts_sec)
    && (tm->ts_nsec == nasd_irix_last_time.ts_nsec))
  {
    nasd_irix_uniq_time.ts_nsec++;
    if (nasd_irix_uniq_time.ts_nsec >= NASD_NSEC_PER_SEC) {
      nasd_irix_uniq_time.ts_nsec -= NASD_NSEC_PER_SEC;
      nasd_irix_uniq_time.ts_sec++;
    }
    *tm = nasd_irix_uniq_time;
  }
  else {
    nasd_irix_last_time = *tm;
    nasd_irix_uniq_time = nasd_irix_last_time;
  }
}

#ifndef KERNEL

volatile nasd_uint32 *nasd_irix_cycle_counter_addr = NULL;
int nasd_irix_mmem_fd = (-1);
size_t nasd_irix_cycle_counter_maplen;
int nasd_irix_cycle_counter_size = 0;
nasd_uint64 nasd_ctimer_ticks;

nasd_uint64
nasd_rpcc_64()
{
#if (_MIPS_SZLONG == 64)
  nasd_uint64 val;

  val = *nasd_irix_cycle_counter_addr;
  return(val);
#else /* (_MIPS_SZLONG == 64) */
  nasd_uint32 *high, *low;
  nasd_uint64 val;

  while(1) {
    *high = *nasd_irix_cycle_counter_addr;
    *low = *(nasd_irix_cycle_counter_addr + 1);
    if (*high == *nasd_irix_cycle_counter_addr) {
      val = *high;
      val <<= 32;
      val |= *low;
      return(val);
    }
  }
#endif /* (_MIPS_SZLONG == 64) */
}

nasd_uint64
nasd_rpcc_32()
{
  nasd_uint32 val;

  val = *nasd_irix_cycle_counter_addr;
  return(val);
}

nasd_status_t
nasd_sys_init()
{
  __psunsigned_t phys_addr, raddr;
  unsigned int cycleval;
  inventory_t *inv;
  int fd, poffmask;

  nasd_irix_mmem_fd = (-1);
  nasd_irix_cycle_counter_addr = NULL;

  nasd_irix_cycle_counter_size = syssgi(SGI_CYCLECNTR_SIZE);

  setinvent();

  do {
    inv = getinvent();
    if (inv == NULL) {
      endinvent();
      return(NASD_FAIL);
    }
    if ((inv->inv_class == INV_PROCESSOR)
      && (inv->inv_type == INV_CPUBOARD))
    {
      /*
       * I'm totally guessing here, but it works on, uh,
       * the one machine I have. --jimz
       */
      nasd_ctimer_ticks = inv->inv_controller;
      nasd_ctimer_ticks *= (1024 * 1024);
      break;
    }
  } while(inv);

  endinvent();

  poffmask = getpagesize() - 1;
  phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
  raddr = phys_addr & ~poffmask;
  fd = open("/dev/mmem", O_RDONLY);
  if (fd < 0) {
    nasd_printf(
      "NASD ERROR: cannot open /dev/mmem to read cycle counter (errno %d)\n",
      errno);
    return(NASD_FAIL);
  }
  nasd_irix_cycle_counter_addr = (volatile nasd_uint32 *)mmap(
    0, poffmask, PROT_READ, MAP_PRIVATE, fd, (off_t)raddr);
  if (nasd_irix_cycle_counter_addr == NULL) {
    close(fd);
    return(NASD_FAIL);
  }

  nasd_irix_cycle_counter_maplen = poffmask;
  nasd_irix_mmem_fd = fd;

  return(NASD_SUCCESS);
}

void
nasd_sys_shutdown()
{
  NASD_ASSERT(nasd_irix_cycle_counter_addr != NULL);
  munmap((void *)nasd_irix_cycle_counter_addr,
    nasd_irix_cycle_counter_maplen);
  nasd_irix_cycle_counter_addr = NULL;

  NASD_ASSERT(nasd_irix_mmem_fd >= 0);
  close(nasd_irix_mmem_fd);
  nasd_irix_mmem_fd = (-1);
}

void
nasd_delay_ts(
  nasd_timespec_t  ts)
{
  struct timeval tv;
  int ret;

  tv.tv_sec = ts.ts_sec;
  tv.tv_usec = ts.ts_nsec / 1000;
  ret = select(1, NULL, NULL, NULL, &tv);
  if (ret < 0)
    NASD_PANIC();
}

nasd_status_t
nasd_get_clock_speed(
  nasd_uint64  *ticksp)
{
  *ticksp = nasd_ctimer_ticks;
  return(NASD_SUCCESS);
}

#endif /* !KERNEL */

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