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.


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