+++ /dev/null
-/*
- * GeekOS timer interrupt support
- * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
- * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
- * $Revision: 1.1 $
- *
- * This is free software. You are permitted to use,
- * redistribute, and modify it as specified in the file "COPYING".
- */
-
-#include <limits.h>
-#include <geekos/io.h>
-#include <geekos/int.h>
-#include <geekos/irq.h>
-#include <geekos/kthread.h>
-#include <geekos/timer.h>
-
-#include <geekos/serial.h>
-
-#define HZ 100
-
-
-/*
- * Global tick counter
- */
-volatile ulong_t g_numTicks;
-
-/*
- * Number of times the spin loop can execute during one timer tick
- */
-static int s_spinCountPerTick;
-
-/*
- * Number of ticks to wait before calibrating the delay loop.
- */
-#define CALIBRATE_NUM_TICKS 3
-
-/*
- * The default quantum; maximum number of ticks a thread can use before
- * we suspend it and choose another.
- */
-#define DEFAULT_MAX_TICKS 4
-
-/*
- * Settable quantum.
- */
-int g_Quantum = DEFAULT_MAX_TICKS;
-
-/*
- * Ticks per second.
- * FIXME: should set this to something more reasonable, like 100.
- */
-#define TICKS_PER_SEC 18
-
-/*#define DEBUG_TIMER */
-#ifdef DEBUG_TIMER
-# define Debug(args...) Print(args)
-#else
-# define Debug(args...)
-#endif
-
-/* ----------------------------------------------------------------------
- * Private functions
- * ---------------------------------------------------------------------- */
-
-static void Timer_Interrupt_Handler(struct Interrupt_State* state)
-{
- struct Kernel_Thread* current = g_currentThread;
-
- Begin_IRQ(state);
-
- SerialPrintLevel(10,"Host Timer Interrupt Handler Running\n");
-
- /* Update global and per-thread number of ticks */
- ++g_numTicks;
- ++current->numTicks;
-
-
- /*
- * If thread has been running for an entire quantum,
- * inform the interrupt return code that we want
- * to choose a new thread.
- */
- if (current->numTicks >= g_Quantum) {
- g_needReschedule = true;
- }
-
-
- End_IRQ(state);
-}
-
-/*
- * Temporary timer interrupt handler used to calibrate
- * the delay loop.
- */
-static void Timer_Calibrate(struct Interrupt_State* state)
-{
- Begin_IRQ(state);
- if (g_numTicks < CALIBRATE_NUM_TICKS)
- ++g_numTicks;
- else {
- /*
- * Now we can look at EAX, which reflects how many times
- * the loop has executed
- */
- /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
- s_spinCountPerTick = INT_MAX - state->eax;
- state->eax = 0; /* make the loop terminate */
- }
- End_IRQ(state);
-}
-
-/*
- * Delay loop; spins for given number of iterations.
- */
-static void Spin(int count)
-{
- /*
- * The assembly code is the logical equivalent of
- * while (count-- > 0) { // waste some time }
- * We rely on EAX being used as the counter
- * variable.
- */
-
- int result;
- __asm__ __volatile__ (
- "1: decl %%eax\n\t"
- "cmpl $0, %%eax\n\t"
- "nop; nop; nop; nop; nop; nop\n\t"
- "nop; nop; nop; nop; nop; nop\n\t"
- "jg 1b"
- : "=a" (result)
- : "a" (count)
- );
-}
-
-/*
- * Calibrate the delay loop.
- * This will initialize s_spinCountPerTick, which indicates
- * how many iterations of the loop are executed per timer tick.
- */
-static void Calibrate_Delay(void)
-{
- Disable_Interrupts();
-
- /* Install temporarily interrupt handler */
- Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
- Enable_IRQ(TIMER_IRQ);
-
- Enable_Interrupts();
-
- /* Wait a few ticks */
- while (g_numTicks < CALIBRATE_NUM_TICKS)
- ;
-
- /*
- * Execute the spin loop.
- * The temporary interrupt handler will overwrite the
- * loop counter when the next tick occurs.
- */
- Spin(INT_MAX);
-
- Disable_Interrupts();
-
- /*
- * Mask out the timer IRQ again,
- * since we will be installing a real timer interrupt handler.
- */
- Disable_IRQ(TIMER_IRQ);
- Enable_Interrupts();
-}
-
-/* ----------------------------------------------------------------------
- * Public functions
- * ---------------------------------------------------------------------- */
-
-void Init_Timer(void)
-{
- ushort_t foo = 1193182L / HZ;
-
- PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
-
- /* Calibrate for delay loop */
- Calibrate_Delay();
- PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
-
- // Set Timer to HZ
-
- Out_Byte(0x43,0x36); // channel 0, LSB/MSB, mode 3, binary
- Out_Byte(0x40, foo & 0xff); // LSB
- Out_Byte(0x40, foo >>8); // MSB
-
- /* Install an interrupt handler for the timer IRQ */
-
- Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
- Enable_IRQ(TIMER_IRQ);
-}
-
-
-#define US_PER_TICK (TICKS_PER_SEC * 1000000)
-
-/*
- * Spin for at least given number of microseconds.
- * FIXME: I'm sure this implementation leaves a lot to
- * be desired.
- */
-void Micro_Delay(int us)
-{
- int num = us * s_spinCountPerTick;
- int denom = US_PER_TICK;
-
- int numSpins = num / denom;
- int rem = num % denom;
-
- if (rem > 0)
- ++numSpins;
-
- Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
-
- Spin(numSpins);
-}