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>
26 volatile ulong_t g_numTicks;
29 * Number of times the spin loop can execute during one timer tick
31 static int s_spinCountPerTick;
34 * Number of ticks to wait before calibrating the delay loop.
36 #define CALIBRATE_NUM_TICKS 3
39 * The default quantum; maximum number of ticks a thread can use before
40 * we suspend it and choose another.
42 #define DEFAULT_MAX_TICKS 4
47 int g_Quantum = DEFAULT_MAX_TICKS;
51 * FIXME: should set this to something more reasonable, like 100.
53 #define TICKS_PER_SEC 18
55 /*#define DEBUG_TIMER */
57 # define Debug(args...) Print(args)
59 # define Debug(args...)
62 /* ----------------------------------------------------------------------
64 * ---------------------------------------------------------------------- */
66 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
68 struct Kernel_Thread* current = g_currentThread;
72 SerialPrintLevel(10,"Host Timer Interrupt Handler Running\n");
74 /* Update global and per-thread number of ticks */
80 * If thread has been running for an entire quantum,
81 * inform the interrupt return code that we want
82 * to choose a new thread.
84 if (current->numTicks >= g_Quantum) {
85 g_needReschedule = true;
93 * Temporary timer interrupt handler used to calibrate
96 static void Timer_Calibrate(struct Interrupt_State* state)
99 if (g_numTicks < CALIBRATE_NUM_TICKS)
103 * Now we can look at EAX, which reflects how many times
104 * the loop has executed
106 /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
107 s_spinCountPerTick = INT_MAX - state->eax;
108 state->eax = 0; /* make the loop terminate */
114 * Delay loop; spins for given number of iterations.
116 static void Spin(int count)
119 * The assembly code is the logical equivalent of
120 * while (count-- > 0) { // waste some time }
121 * We rely on EAX being used as the counter
126 __asm__ __volatile__ (
129 "nop; nop; nop; nop; nop; nop\n\t"
130 "nop; nop; nop; nop; nop; nop\n\t"
138 * Calibrate the delay loop.
139 * This will initialize s_spinCountPerTick, which indicates
140 * how many iterations of the loop are executed per timer tick.
142 static void Calibrate_Delay(void)
144 Disable_Interrupts();
146 /* Install temporarily interrupt handler */
147 Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
148 Enable_IRQ(TIMER_IRQ);
152 /* Wait a few ticks */
153 while (g_numTicks < CALIBRATE_NUM_TICKS)
157 * Execute the spin loop.
158 * The temporary interrupt handler will overwrite the
159 * loop counter when the next tick occurs.
163 Disable_Interrupts();
166 * Mask out the timer IRQ again,
167 * since we will be installing a real timer interrupt handler.
169 Disable_IRQ(TIMER_IRQ);
173 /* ----------------------------------------------------------------------
175 * ---------------------------------------------------------------------- */
177 void Init_Timer(void)
179 ushort_t foo = 1193182L / HZ;
181 PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
183 /* 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);