* 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.3 $
+ * (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ * (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * $Revision: 1.11 $
*
* This is free software. You are permitted to use,
* redistribute, and modify it as specified in the file "COPYING".
#include <geekos/serial.h>
#include <geekos/debug.h>
-#define HZ 100
+#include <geekos/io_defs.h>
+
+/* PAD this currently is in nvram.c */
+extern void deliver_timer_interrupt_to_vmm(uint_t period_us);
+
+/* JRL Add a cpu frequency measurement */
+uint_t cpu_khz_freq;
+
+
+
+
+
+
+
+
+
+
+#if defined(__i386__)
+
+#define rdtscll(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+
+#elif defined(__x86_64__)
+
+#define rdtscll(val) do { \
+ unsigned int a,d; \
+ asm volatile("rdtsc" : "=a" (a), "=d" (d)); \
+ (val) = ((unsigned long)a) | (((unsigned long)d)<<32); \
+ } while(0)
+
+#endif
+
+#define do_div(n,base) ({ \
+ unsigned long __upper, __low, __high, __mod, __base; \
+ __base = (base); \
+ asm("":"=a" (__low), "=d" (__high):"A" (n)); \
+ __upper = __high; \
+ if (__high) { \
+ __upper = __high % (__base); \
+ __high = __high / (__base); \
+ } \
+ asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
+ asm("":"=A" (n):"a" (__low),"d" (__high)); \
+ __mod; \
+ })
+
+
+/**
+ * This uses the Programmable Interval Timer that is standard on all
+ * PC-compatible systems to determine the time stamp counter frequency.
+ *
+ * This uses the speaker output (channel 2) of the PIT. This is better than
+ * using the timer interrupt output because we can read the value of the
+ * speaker with just one inb(), where we need three i/o operations for the
+ * interrupt channel. We count how many ticks the TSC does in 50 ms.
+ *
+ * Returns the detected time stamp counter frequency in KHz.
+ */
+
+
+static unsigned int
+pit_calibrate_tsc(void)
+{
+ uint_t khz = 0;
+ ullong_t start, end;
+ // unsigned long flags;
+ unsigned long pit_tick_rate = 1193182UL; /* 1.193182 MHz */
+
+ // spin_lock_irqsave(&pit_lock, flags);
+
+ outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+
+ outb(0xb0, 0x43);
+ outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42);
+ outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42);
+ // start = get_cycles_sync();
+ rdtscll(start);
+ while ((inb(0x61) & 0x20) == 0);
+ rdtscll(end);
+ // end = get_cycles_sync();
+
+ // spin_unlock_irqrestore(&pit_lock, flags);
+
+
+ // return (end - start) / 50;
+ khz = end - start;
+;
+
+ return khz / 50;
+}
+
+
+
+/* END JRL */
+
+
+
+#define MAX_TIMER_EVENTS 100
+
+static int timerDebug = 0;
+static int timeEventCount;
+static int nextEventID;
+timerEvent pendingTimerEvents[MAX_TIMER_EVENTS];
+
/*
*/
volatile ulong_t g_numTicks;
+ulong_t clock_time(void){
+ return g_numTicks;
+}
+
+
/*
* Number of times the spin loop can execute during one timer tick
*/
* Ticks per second.
* FIXME: should set this to something more reasonable, like 100.
*/
-#define TICKS_PER_SEC 18
+#define HZ 100
+//#define TICKS_PER_SEC 18
/*#define DEBUG_TIMER */
#ifdef DEBUG_TIMER
static void Timer_Interrupt_Handler(struct Interrupt_State* state)
{
- struct Kernel_Thread* current = g_currentThread;
-
- Begin_IRQ(state);
+ int i;
+ struct Kernel_Thread* current = g_currentThread;
- SerialPrintLevel(1,"Host Timer Interrupt Handler Running\n");
-
- /* Update global and per-thread number of ticks */
- ++g_numTicks;
- ++current->numTicks;
+ Begin_IRQ(state);
+ /* Update global and per-thread number of ticks */
+ ++g_numTicks;
+ ++current->numTicks;
+
+ /* update timer events */
+ for (i=0; i < timeEventCount; i++) {
+ if (pendingTimerEvents[i].ticks == 0) {
+ if (timerDebug) Print("timer: event %d expired (%d ticks)\n",
+ pendingTimerEvents[i].id, pendingTimerEvents[i].origTicks);
+ (pendingTimerEvents[i].callBack)(pendingTimerEvents[i].id);
+ pendingTimerEvents[i].ticks = pendingTimerEvents[i].origTicks;
+ } else {
+ pendingTimerEvents[i].ticks--;
+ }
+ }
+
+ /*
+ * 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;
/*
- * If thread has been running for an entire quantum,
- * inform the interrupt return code that we want
- * to choose a new thread.
+ * The current process is moved to a lower priority queue,
+ * since it consumed a full quantum.
*/
- if (current->numTicks >= g_Quantum) {
- g_needReschedule = true;
- }
-
-
- End_IRQ(state);
+ //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) {
+ /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */
+ //current->currentReadyQueue++;
+ //}
+
+ }
+
+
+ deliver_timer_interrupt_to_vmm(1000000/HZ);
+
+ End_IRQ(state);
}
/*
;
/*
- * Execute the spin loop.
+ * Execute the spin loop.xs
* The temporary interrupt handler will overwrite the
* loop counter when the next tick occurs.
*/
+
+
Spin(INT_MAX);
+
+
Disable_Interrupts();
/*
{
ushort_t foo = 1193182L / HZ;
+ cpu_khz_freq = pit_calibrate_tsc();
+ PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq);
+
PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
/* Calibrate for delay loop */
}
-#define US_PER_TICK (TICKS_PER_SEC * 1000000)
+int Start_Timer(int ticks, timerCallback cb)
+{
+ int ret;
+
+ KASSERT(!Interrupts_Enabled());
+
+ if (timeEventCount == MAX_TIMER_EVENTS) {
+ return -1;
+ } else {
+ ret = nextEventID++;
+ pendingTimerEvents[timeEventCount].id = ret;
+ pendingTimerEvents[timeEventCount].callBack = cb;
+ pendingTimerEvents[timeEventCount].ticks = ticks;
+ pendingTimerEvents[timeEventCount].origTicks = ticks;
+ timeEventCount++;
+
+ return ret;
+ }
+}
+
+int Get_Remaing_Timer_Ticks(int id)
+{
+ int i;
+
+ KASSERT(!Interrupts_Enabled());
+ for (i=0; i < timeEventCount; i++) {
+ if (pendingTimerEvents[i].id == id) {
+ return pendingTimerEvents[i].ticks;
+ }
+ }
+
+ return -1;
+}
+
+int Cancel_Timer(int id)
+{
+ int i;
+ KASSERT(!Interrupts_Enabled());
+ for (i=0; i < timeEventCount; i++) {
+ if (pendingTimerEvents[i].id == id) {
+ pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1];
+ timeEventCount--;
+ return 0;
+ }
+ }
+
+ Print("timer: unable to find timer id %d to cancel it\n", id);
+ return -1;
+}
+
+
+#define US_PER_TICK (HZ * 1000000)
/*
* Spin for at least given number of microseconds.