Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


113a17f3435041baafa75eac360dbbac2a5606fd
[palacios.git] / palacios / src / geekos / timer.c
1 /*
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  * $Revision: 1.3 $
6  * 
7  * This is free software.  You are permitted to use,
8  * redistribute, and modify it as specified in the file "COPYING".
9  */
10
11 #include <limits.h>
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>
17
18 #include <geekos/serial.h>
19 #include <geekos/debug.h>
20
21 #define HZ 100
22
23
24 /*
25  * Global tick counter
26  */
27 volatile ulong_t g_numTicks;
28
29 /*
30  * Number of times the spin loop can execute during one timer tick
31  */
32 static int s_spinCountPerTick;
33
34 /*
35  * Number of ticks to wait before calibrating the delay loop.
36  */
37 #define CALIBRATE_NUM_TICKS     3
38
39 /*
40  * The default quantum; maximum number of ticks a thread can use before
41  * we suspend it and choose another.
42  */
43 #define DEFAULT_MAX_TICKS 4
44
45 /*
46  * Settable quantum.
47  */
48 int g_Quantum = DEFAULT_MAX_TICKS;
49
50 /*
51  * Ticks per second.
52  * FIXME: should set this to something more reasonable, like 100.
53  */
54 #define TICKS_PER_SEC 18
55
56 /*#define DEBUG_TIMER */
57 #ifdef DEBUG_TIMER
58 #  define Debug(args...) Print(args)
59 #else
60 #  define Debug(args...)
61 #endif
62
63 /* ----------------------------------------------------------------------
64  * Private functions
65  * ---------------------------------------------------------------------- */
66
67 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
68 {
69     struct Kernel_Thread* current = g_currentThread;
70
71     Begin_IRQ(state);
72
73     SerialPrintLevel(1,"Host Timer Interrupt Handler Running\n");
74
75     /* Update global and per-thread number of ticks */
76     ++g_numTicks;
77     ++current->numTicks;
78
79
80     /*
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.
84      */
85     if (current->numTicks >= g_Quantum) {
86         g_needReschedule = true;
87     }
88
89
90     End_IRQ(state);
91 }
92
93 /*
94  * Temporary timer interrupt handler used to calibrate
95  * the delay loop.
96  */
97 static void Timer_Calibrate(struct Interrupt_State* state)
98 {
99     Begin_IRQ(state);
100     if (g_numTicks < CALIBRATE_NUM_TICKS)
101         ++g_numTicks;
102     else {
103         /*
104          * Now we can look at EAX, which reflects how many times
105          * the loop has executed
106          */
107         /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
108         s_spinCountPerTick = INT_MAX  - state->eax;
109         state->eax = 0;  /* make the loop terminate */
110     }
111     End_IRQ(state);
112 }
113
114 /*
115  * Delay loop; spins for given number of iterations.
116  */
117 static void Spin(int count)
118 {
119     /*
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
123      * variable.
124      */
125
126     int result;
127     __asm__ __volatile__ (
128         "1: decl %%eax\n\t"
129         "cmpl $0, %%eax\n\t"
130         "nop; nop; nop; nop; nop; nop\n\t"
131         "nop; nop; nop; nop; nop; nop\n\t"
132         "jg 1b"
133         : "=a" (result)
134         : "a" (count)
135     );
136 }
137
138 /*
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.
142  */
143 static void Calibrate_Delay(void)
144 {
145     Disable_Interrupts();
146
147     /* Install temporarily interrupt handler */
148     Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
149     Enable_IRQ(TIMER_IRQ);
150
151     Enable_Interrupts();
152
153     /* Wait a few ticks */
154     while (g_numTicks < CALIBRATE_NUM_TICKS)
155         ;
156
157     /*
158      * Execute the spin loop.
159      * The temporary interrupt handler will overwrite the
160      * loop counter when the next tick occurs.
161      */
162     Spin(INT_MAX);
163
164     Disable_Interrupts();
165
166     /*
167      * Mask out the timer IRQ again,
168      * since we will be installing a real timer interrupt handler.
169      */
170     Disable_IRQ(TIMER_IRQ);
171     Enable_Interrupts();
172 }
173
174 /* ----------------------------------------------------------------------
175  * Public functions
176  * ---------------------------------------------------------------------- */
177
178 void Init_Timer(void)
179 {
180   ushort_t foo = 1193182L / HZ;
181   
182   PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
183   
184   /* Calibrate for delay loop */
185   Calibrate_Delay();
186   PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
187   
188   // Set Timer to HZ
189
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
193     
194   /* Install an interrupt handler for the timer IRQ */
195
196   Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
197   Enable_IRQ(TIMER_IRQ);
198 }
199
200
201 #define US_PER_TICK (TICKS_PER_SEC * 1000000)
202
203 /*
204  * Spin for at least given number of microseconds.
205  * FIXME: I'm sure this implementation leaves a lot to
206  * be desired.
207  */
208 void Micro_Delay(int us)
209 {
210     int num = us * s_spinCountPerTick;
211     int denom = US_PER_TICK;
212
213     int numSpins = num / denom;
214     int rem = num % denom;
215
216     if (rem > 0)
217         ++numSpins;
218
219     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
220
221     Spin(numSpins);
222 }