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>
5 * (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
6 * (c) 2008, The V3VEE Project <http://www.v3vee.org>
9 * This is free software. You are permitted to use,
10 * redistribute, and modify it as specified in the file "COPYING".
14 #include <geekos/io.h>
15 #include <geekos/int.h>
16 #include <geekos/irq.h>
17 #include <geekos/kthread.h>
18 #include <geekos/timer.h>
20 #include <geekos/serial.h>
21 #include <geekos/debug.h>
23 #include <geekos/io_defs.h>
25 #include <geekos/vmm_stubs.h>
27 /* PAD this currently is in nvram.c */
28 /* JRL: This is completely broken
29 extern void deliver_timer_interrupt_to_vmm(uint_t period_us);
32 /* JRL Add a cpu frequency measurement */
46 #define rdtscll(val) \
47 __asm__ __volatile__("rdtsc" : "=A" (val))
49 #elif defined(__x86_64__)
51 #define rdtscll(val) do { \
53 asm volatile("rdtsc" : "=a" (a), "=d" (d)); \
54 (val) = ((unsigned long)a) | (((unsigned long)d)<<32); \
59 #define do_div(n,base) ({ \
60 unsigned long __upper, __low, __high, __mod, __base; \
62 asm("":"=a" (__low), "=d" (__high):"A" (n)); \
65 __upper = __high % (__base); \
66 __high = __high / (__base); \
68 asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
69 asm("":"=A" (n):"a" (__low),"d" (__high)); \
75 * This uses the Programmable Interval Timer that is standard on all
76 * PC-compatible systems to determine the time stamp counter frequency.
78 * This uses the speaker output (channel 2) of the PIT. This is better than
79 * using the timer interrupt output because we can read the value of the
80 * speaker with just one inb(), where we need three i/o operations for the
81 * interrupt channel. We count how many ticks the TSC does in 50 ms.
83 * Returns the detected time stamp counter frequency in KHz.
88 pit_calibrate_tsc(void)
92 // unsigned long flags;
93 unsigned long pit_tick_rate = 1193182UL; /* 1.193182 MHz */
95 // spin_lock_irqsave(&pit_lock, flags);
97 outb((inb(0x61) & ~0x02) | 0x01, 0x61);
100 outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42);
101 outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42);
102 // start = get_cycles_sync();
104 while ((inb(0x61) & 0x20) == 0);
106 // end = get_cycles_sync();
108 // spin_unlock_irqrestore(&pit_lock, flags);
111 // return (end - start) / 50;
124 #define MAX_TIMER_EVENTS 100
126 static int timerDebug = 0;
127 static int timeEventCount;
128 static int nextEventID;
129 timerEvent pendingTimerEvents[MAX_TIMER_EVENTS];
134 * Global tick counter
136 volatile ulong_t g_numTicks;
139 * Number of times the spin loop can execute during one timer tick
141 static int s_spinCountPerTick;
144 * Number of ticks to wait before calibrating the delay loop.
146 #define CALIBRATE_NUM_TICKS 3
149 * The default quantum; maximum number of ticks a thread can use before
150 * we suspend it and choose another.
152 #define DEFAULT_MAX_TICKS 4
157 int g_Quantum = DEFAULT_MAX_TICKS;
161 * FIXME: should set this to something more reasonable, like 100.
164 //#define TICKS_PER_SEC 18
166 /*#define DEBUG_TIMER */
168 # define Debug(args...) Print(args)
170 # define Debug(args...)
173 ulong_t clock_time(void){//in millisec
174 return g_numTicks * (1000/HZ);
177 /* ----------------------------------------------------------------------
179 * ---------------------------------------------------------------------- */
181 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
184 struct Kernel_Thread* current = g_currentThread;
188 /* Update global and per-thread number of ticks */
193 /* update timer events */
194 for (i=0; i < timeEventCount; i++) {
195 if (pendingTimerEvents[i].ticks == 0) {
196 if (timerDebug) Print("timer: event %d expired (%d ticks)\n",
197 pendingTimerEvents[i].id, pendingTimerEvents[i].origTicks);
198 (pendingTimerEvents[i].callBack)(pendingTimerEvents[i].id, pendingTimerEvents[i].cb_arg);
199 pendingTimerEvents[i].ticks = pendingTimerEvents[i].origTicks;
201 pendingTimerEvents[i].ticks--;
206 * If thread has been running for an entire quantum,
207 * inform the interrupt return code that we want
208 * to choose a new thread.
210 if (current->numTicks >= g_Quantum) {
211 g_needReschedule = true;
213 * The current process is moved to a lower priority queue,
214 * since it consumed a full quantum.
216 //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) {
217 /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */
218 //current->currentReadyQueue++;
224 send_tick_to_vmm(1000000/HZ);
230 * Temporary timer interrupt handler used to calibrate
233 static void Timer_Calibrate(struct Interrupt_State* state)
236 if (g_numTicks < CALIBRATE_NUM_TICKS)
240 * Now we can look at EAX, which reflects how many times
241 * the loop has executed
243 /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
244 s_spinCountPerTick = INT_MAX - state->eax;
245 state->eax = 0; /* make the loop terminate */
251 * Delay loop; spins for given number of iterations.
253 static void Spin(int count)
256 * The assembly code is the logical equivalent of
257 * while (count-- > 0) { // waste some time }
258 * We rely on EAX being used as the counter
263 __asm__ __volatile__ (
266 "nop; nop; nop; nop; nop; nop\n\t"
267 "nop; nop; nop; nop; nop; nop\n\t"
275 * Calibrate the delay loop.
276 * This will initialize s_spinCountPerTick, which indicates
277 * how many iterations of the loop are executed per timer tick.
279 static void Calibrate_Delay(void)
281 Disable_Interrupts();
283 /* Install temporarily interrupt handler */
284 Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
285 Enable_IRQ(TIMER_IRQ);
289 /* Wait a few ticks */
290 while (g_numTicks < CALIBRATE_NUM_TICKS)
294 * Execute the spin loop.xs
295 * The temporary interrupt handler will overwrite the
296 * loop counter when the next tick occurs.
304 Disable_Interrupts();
307 * Mask out the timer IRQ again,
308 * since we will be installing a real timer interrupt handler.
310 Disable_IRQ(TIMER_IRQ);
314 /* ----------------------------------------------------------------------
316 * ---------------------------------------------------------------------- */
318 void Init_Timer(void)
320 ushort_t foo = 1193182L / HZ;
322 cpu_khz_freq = pit_calibrate_tsc();
323 PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq);
325 PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
327 /* Calibrate for delay loop */
329 PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
333 Out_Byte(0x43,0x36); // channel 0, LSB/MSB, mode 3, binary
334 Out_Byte(0x40, foo & 0xff); // LSB
335 Out_Byte(0x40, foo >>8); // MSB
337 /* Install an interrupt handler for the timer IRQ */
339 Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
340 Enable_IRQ(TIMER_IRQ);
344 int Start_Timer_Secs(int seconds, timerCallback cb, void * arg) {
345 return Start_Timer(seconds * HZ, cb, arg);
349 int Start_Timer_MSecs(int msecs, timerCallback cb, void * arg) {
350 msecs += 10 - (msecs % 10);
352 return Start_Timer(msecs * (HZ / 1000), cb, arg);
357 int Start_Timer(int ticks, timerCallback cb, void * arg)
361 KASSERT(!Interrupts_Enabled());
363 PrintBoth ("there\n");
365 if (timeEventCount == MAX_TIMER_EVENTS) {
369 pendingTimerEvents[timeEventCount].id = ret;
370 pendingTimerEvents[timeEventCount].callBack = cb;
371 pendingTimerEvents[timeEventCount].cb_arg = arg;
372 pendingTimerEvents[timeEventCount].ticks = ticks;
373 pendingTimerEvents[timeEventCount].origTicks = ticks;
381 int Get_Remaining_Timer_Ticks(int id)
385 KASSERT(!Interrupts_Enabled());
386 for (i=0; i < timeEventCount; i++) {
387 if (pendingTimerEvents[i].id == id) {
388 return pendingTimerEvents[i].ticks;
397 double Get_Remaining_Timer_Secs(int id) {
398 return (Get_Remaining_Timer_Ticks(id) / HZ);
402 int Get_Remaining_Timer_MSecs(int id) {
403 return ((Get_Remaining_Timer_Ticks(id) * 1000) / HZ);
408 int Cancel_Timer(int id)
411 KASSERT(!Interrupts_Enabled());
412 for (i=0; i < timeEventCount; i++) {
413 if (pendingTimerEvents[i].id == id) {
414 pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1];
420 Print("timer: unable to find timer id %d to cancel it\n", id);
425 #define US_PER_TICK (HZ * 1000000)
428 * Spin for at least given number of microseconds.
429 * FIXME: I'm sure this implementation leaves a lot to
432 void Micro_Delay(int us)
434 int num = us * s_spinCountPerTick;
435 int denom = US_PER_TICK;
437 int numSpins = num / denom;
438 int rem = num % denom;
443 Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);