/*
    TMClock.c, Copyright 18.3.97 (c) by C. Conrad & G. Fankhauser,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Threads/mips/TMClock.c,v $
 	Author(s):             C. Conrad & G. Fankhauser
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2936 $
 	Creation Date:         18.3.97
 	Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
	
	
	$Log: TMClock.c,v $
	Revision 1.12  2000/11/15 10:13:18  ruf
	Repository Reorganisation
	
	Revision 1.11  1999/12/13 21:48:35  ruf
	GNU General Public Licence Update
	
	Revision 1.10  1999/10/21 20:22:30  jeker
	first commit for the R4k support
	
	Revision 1.9  1999/01/08 18:56:35  cjeker
	more code cleaning, moved tmResetClockInterrupt to the clock interrupt handlers
 
	Revision 1.8  1998/06/05 14:37:57  gfa
	added get/set time
	
	Revision 1.7  1998/03/31 17:13:10  gries
	support for mode4 added
 
	Revision 1.6  1997/04/18 16:28:37  conrad
	*** empty log message ***
 
 * Revision 1.5  1997/04/17  11:33:49  conrad
 * *** empty log message ***
 *
 * Revision 1.4  1997/04/17  10:56:16  conrad
 * use of ioDelayAtLeastCycles between hw accesses
 *
 * Revision 1.3  1997/04/14  21:09:10  conrad
 * *** empty log message ***
 *
 * Revision 1.2  1997/04/14  12:32:58  conrad
 * adding of a function to reset clock interrupts
 *
 * Revision 1.1  1997/03/18  17:37:55  conrad
 * Initial revision
 *
*/

#include "TMHal.h"
#include "IOHal.h"
#include "IODevice.h"
#include "TMScheduler.h"
#include "Support.h"
#include "TMClock.h"


/* 8254 Register Addressing structure */
typedef struct TimerRegisters_t
{
  unsigned char counter0;
  unsigned char pad1[3];
  unsigned char counter1;
  unsigned char pad2[3];
  unsigned char counter2;
  unsigned char pad3[3];
  unsigned char ctrlWord;
  unsigned char pad4[3];
}
TimerRegisters;

/*
 * This function initializes the clock on the board.
 */
Error setClockValue( ClockId cId,
                     int period,
                     ClockMode cMode)
{
#ifndef SIMOS
  TimerRegisters* timerRegisters = (TimerRegisters*)TIMERBASE;
  unsigned short int initialCount = 0;  /* count for counter 0 or 1 */
  unsigned short int initialCount2 = 100;    /* count for counter 2 */
  int frequencyCoeff = 3686 / initialCount2;
  /* Cristal oscillator frequency: 3.6861 MHz.
     Period given in [ms], -> #ticks = 1000*period[ms]*freq[MHz].
     As there are two cascaded timers (c2-c0 or c2-c1), each counter
     should ideally have an initial value of sqrt(initialCount) */
  initialCount = period * frequencyCoeff;

  if (cMode == RATEGENERATOR)
  {  /* single mode supported */
    switch (cId)
    {
    case CLOCK0:
      timerRegisters->ctrlWord = (unsigned char)MODE2_COUNTER0;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter0 = (unsigned char)initialCount;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter0 = (unsigned char)(initialCount >> 8);
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      break;
    case CLOCK1:
      timerRegisters->ctrlWord = MODE2_COUNTER1;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter1 = (unsigned char)initialCount;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter1 = (unsigned char)(initialCount >> 8);
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      break;
    }
    timerRegisters->ctrlWord = MODE2_COUNTER2;
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    timerRegisters->counter2 = (unsigned char)initialCount2;
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    timerRegisters->counter2 = (unsigned char)(initialCount2 >> 8);
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  }
  else if (cMode == SWTRIGGER)
  {  /* intel 8254 mode4 */
    switch (cId)
    {
    case CLOCK0:
      timerRegisters->ctrlWord = (unsigned char)MODE4_COUNTER0;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter0 = (unsigned char)initialCount;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter0 = (unsigned char)(initialCount >> 8);
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      break;
    case CLOCK1:
      timerRegisters->ctrlWord = MODE4_COUNTER1;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter1 = (unsigned char)initialCount;
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      timerRegisters->counter1 = (unsigned char)(initialCount >> 8);
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
      break;
    }
    timerRegisters->ctrlWord = MODE2_COUNTER2; /* CLOCK2 must use mode2 */
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    timerRegisters->counter2 = (unsigned char)initialCount2;
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    timerRegisters->counter2 = (unsigned char)(initialCount2 >> 8);
    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  }
#else

  /* SimOS emulates a preaty sophisticated MAGIC chipset, with support
      * for 64 interrupt sources and other goodies we don't care about.
      * To make my life enjoyable, none of those is documented anywhere,
      * so I just utilize a bare mimimum requied to get the scheduler going.
   */
  if (cId != CLOCK0)
    PANIC("SimOS does not support timers other than CLOCK0");

  /* Set up the interval timer. */
  magic_control[MAGIC_PPR_IECENABLE] |= 1 << DEV_IEC_CLOCK;
  magic_control[MAGIC_PPR_INTERVAL]   = TIMESLICE;

#endif

  return TM_OK;
}

/* When a clock interrupt is generated, the clock must be stoped. So a reset is performed.
 * If not done, the interrupt is generated at once again and again ...
 */
void tmResetClockInterrupt(ClockId cId)
{
#ifndef SIMOS
  char* x;
  char y;

  switch (cId)
  {
  case CLOCK0:
    x = (char*)RESETCINT0;
    break;
  case CLOCK1:
    x = (char*)RESETCINT1;
    break;
  default:
    return;
  }
  y = *x;
#else /* SIMOS */

  if (cId != CLOCK0)
    PANIC("SimOS does not support timers other than CLOCK0");

  magic_control[MAGIC_PPR_IECPENDING] = 1 << DEV_IEC_CLOCK;

#endif

}

/* since we don't have an RTC installed we don't do anything here
 * otherwise we would calculate the seconds 
 * since January 1st 1998, 00:00, UTC, and return or set them
 */
unsigned long tmRTClockGetSeconds()
{
  return 0;
}

void tmRTClockSetSeconds(unsigned long seconds, unsigned long microSeconds)
{
  return;
}

