2 * GeekOS timer interrupt support
3 * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
4 * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
7 * This is free software. You are permitted to use,
8 * redistribute, and modify it as specified in the file "COPYING".
12 #include <geekos/io.h>
13 #include <geekos/int.h>
14 #include <geekos/irq.h>
15 #include <geekos/kthread.h>
16 #include <geekos/timer.h>
18 #include <geekos/serial.h>
19 #include <geekos/debug.h>
27 volatile ulong_t g_numTicks;
30 * Number of times the spin loop can execute during one timer tick
32 static int s_spinCountPerTick;
35 * Number of ticks to wait before calibrating the delay loop.
37 #define CALIBRATE_NUM_TICKS 3
40 * The default quantum; maximum number of ticks a thread can use before
41 * we suspend it and choose another.
43 #define DEFAULT_MAX_TICKS 4
48 int g_Quantum = DEFAULT_MAX_TICKS;
52 * FIXME: should set this to something more reasonable, like 100.
54 #define TICKS_PER_SEC 18
56 /*#define DEBUG_TIMER */
58 # define Debug(args...) Print(args)
60 # define Debug(args...)
63 /* ----------------------------------------------------------------------
65 * ---------------------------------------------------------------------- */
67 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
69 struct Kernel_Thread* current = g_currentThread;
73 SerialPrintLevel(1,"Host Timer Interrupt Handler Running\n");
75 /* Update global and per-thread number of ticks */
81 * If thread has been running for an entire quantum,
82 * inform the interrupt return code that we want
83 * to choose a new thread.
85 if (current->numTicks >= g_Quantum) {
86 g_needReschedule = true;
94 * Temporary timer interrupt handler used to calibrate
97 static void Timer_Calibrate(struct Interrupt_State* state)
100 if (g_numTicks < CALIBRATE_NUM_TICKS)
104 * Now we can look at EAX, which reflects how many times
105 * the loop has executed
107 /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
108 s_spinCountPerTick = INT_MAX - state->eax;
109 state->eax = 0; /* make the loop terminate */
115 * Delay loop; spins for given number of iterations.
117 static void Spin(int count)
120 * The assembly code is the logical equivalent of
121 * while (count-- > 0) { // waste some time }
122 * We rely on EAX being used as the counter
127 __asm__ __volatile__ (
130 "nop; nop; nop; nop; nop; nop\n\t"
131 "nop; nop; nop; nop; nop; nop\n\t"
139 * Calibrate the delay loop.
140 * This will initialize s_spinCountPerTick, which indicates
141 * how many iterations of the loop are executed per timer tick.
143 static void Calibrate_Delay(void)
145 Disable_Interrupts();
147 /* Install temporarily interrupt handler */
148 Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
149 Enable_IRQ(TIMER_IRQ);
153 /* Wait a few ticks */
154 while (g_numTicks < CALIBRATE_NUM_TICKS)
158 * Execute the spin loop.
159 * The temporary interrupt handler will overwrite the
160 * loop counter when the next tick occurs.
164 Disable_Interrupts();
167 * Mask out the timer IRQ again,
168 * since we will be installing a real timer interrupt handler.
170 Disable_IRQ(TIMER_IRQ);
174 /* ----------------------------------------------------------------------
176 * ---------------------------------------------------------------------- */
178 void Init_Timer(void)
180 ushort_t foo = 1193182L / HZ;
182 PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
184 /* Calibrate for delay loop */
186 PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
190 Out_Byte(0x43,0x36); // channel 0, LSB/MSB, mode 3, binary
191 Out_Byte(0x40, foo & 0xff); // LSB
192 Out_Byte(0x40, foo >>8); // MSB
194 /* Install an interrupt handler for the timer IRQ */
196 Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
197 Enable_IRQ(TIMER_IRQ);
201 #define US_PER_TICK (TICKS_PER_SEC * 1000000)
204 * Spin for at least given number of microseconds.
205 * FIXME: I'm sure this implementation leaves a lot to
208 void Micro_Delay(int us)
210 int num = us * s_spinCountPerTick;
211 int denom = US_PER_TICK;
213 int numSpins = num / denom;
214 int rem = num % denom;
219 Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);