- Remove KeAttachProcess and KeDetachProcess prototypes from winddk.h.
[reactos.git] / reactos / hal / halx86 / generic / timer.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2000 David Welch <welch@cwcom.net>
4 * Copyright (C) 1999 Gareth Owen <gaz@athene.co.uk>, Ramon von Handel
5 * Copyright (C) 1991, 1992 Linus Torvalds
6 *
7 * This software is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this software; see the file COPYING. If not, write
19 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
20 * MA 02139, USA.
21 *
22 */
23 /* $Id$
24 *
25 * PROJECT: ReactOS kernel
26 * FILE: ntoskrnl/hal/x86/udelay.c
27 * PURPOSE: Busy waiting
28 * PROGRAMMER: David Welch (david.welch@seh.ox.ac.uk)
29 * UPDATE HISTORY:
30 * 06/11/99 Created
31 */
32
33 /* INCLUDES ***************************************************************/
34
35 #include <ddk/ntddk.h>
36 #include <internal/ob.h>
37 #include <internal/ps.h>
38 #include <hal.h>
39
40 #define NDEBUG
41 #include <internal/debug.h>
42
43 /* GLOBALS ******************************************************************/
44
45 #define TMR_CTRL 0x43 /* I/O for control */
46 #define TMR_CNT0 0x40 /* I/O for counter 0 */
47 #define TMR_CNT1 0x41 /* I/O for counter 1 */
48 #define TMR_CNT2 0x42 /* I/O for counter 2 */
49
50 #define TMR_SC0 0 /* Select channel 0 */
51 #define TMR_SC1 0x40 /* Select channel 1 */
52 #define TMR_SC2 0x80 /* Select channel 2 */
53
54 #define TMR_LOW 0x10 /* RW low byte only */
55 #define TMR_HIGH 0x20 /* RW high byte only */
56 #define TMR_BOTH 0x30 /* RW both bytes */
57
58 #define TMR_MD0 0 /* Mode 0 */
59 #define TMR_MD1 0x2 /* Mode 1 */
60 #define TMR_MD2 0x4 /* Mode 2 */
61 #define TMR_MD3 0x6 /* Mode 3 */
62 #define TMR_MD4 0x8 /* Mode 4 */
63 #define TMR_MD5 0xA /* Mode 5 */
64
65 #define TMR_BCD 1 /* BCD mode */
66
67 #define TMR_LATCH 0 /* Latch command */
68
69 #define TMR_READ 0xF0 /* Read command */
70 #define TMR_CNT 0x20 /* CNT bit (Active low, subtract it) */
71 #define TMR_STAT 0x10 /* Status bit (Active low, subtract it) */
72 #define TMR_CH2 0x8 /* Channel 2 bit */
73 #define TMR_CH1 0x4 /* Channel 1 bit */
74 #define TMR_CH0 0x2 /* Channel 0 bit */
75
76 #define MILLISEC 10 /* Number of millisec between interrupts */
77 #define HZ (1000 / MILLISEC) /* Number of interrupts per second */
78 #define CLOCK_TICK_RATE 1193182 /* Clock frequency of the timer chip */
79 #define LATCH (CLOCK_TICK_RATE / HZ) /* Count to program into the timer chip */
80 #define PRECISION 8 /* Number of bits to calibrate for delay loop */
81
82 static BOOLEAN UdelayCalibrated = FALSE;
83
84 /* FUNCTIONS **************************************************************/
85
86 /*
87 * NOTE: This function MUST NOT be optimized by the compiler!
88 * If it is, it obviously will not delay AT ALL, and the system
89 * will appear completely frozen at boot since
90 * HalpCalibrateStallExecution will never return.
91 * There are three options to stop optimization:
92 * 1. Use a volatile automatic variable. Making it delay quite a bit
93 * due to memory accesses, and keeping the code portable. However,
94 * as this involves memory access it depends on both the CPU cache,
95 * e.g. if the stack used is already in a cache line or not, and
96 * whether or not we're MP. If MP, another CPU could (probably would)
97 * also access RAM at the same time - making the delay imprecise.
98 * 2. Use compiler-specific #pragma's to disable optimization.
99 * 3. Use inline assembly, making it equally unportable as #2.
100 * For supported compilers we use inline assembler. For the others,
101 * portable plain C.
102 */
103 VOID STDCALL __attribute__((noinline))
104 __KeStallExecutionProcessor(ULONG Loops)
105 {
106 if (!Loops)
107 {
108 return;
109 }
110 #if defined(__GNUC__)
111 __asm__ __volatile__ (
112 "mov %0, %%eax\n"
113 "ROSL1: dec %%eax\n"
114 "jnz ROSL1" : : "d" (Loops));
115
116 #elif defined(_MSC_VER)
117 __asm mov eax, Loops
118 ROSL1:
119 __asm dec eax
120 __asm jnz ROSL1
121 #else
122 volatile unsigned int target = Loops;
123 unsigned int i;
124 for (i=0; i<target;i++);
125 #endif
126 }
127
128 VOID STDCALL KeStallExecutionProcessor(ULONG Microseconds)
129 {
130 PKPCR Pcr = KeGetCurrentKPCR();
131
132 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC)
133 {
134 LARGE_INTEGER EndCount, CurrentCount;
135 Ki386RdTSC(EndCount);
136 EndCount.QuadPart += Microseconds * (ULONGLONG)Pcr->PrcbData.MHz;
137 do
138 {
139 Ki386RdTSC(CurrentCount);
140 }
141 while (CurrentCount.QuadPart < EndCount.QuadPart);
142 }
143 else
144 {
145 __KeStallExecutionProcessor((Pcr->StallScaleFactor*Microseconds)/1000);
146 }
147 }
148
149 static ULONG Read8254Timer(VOID)
150 {
151 ULONG Count;
152 ULONG flags;
153
154 /* save flags and disable interrupts */
155 Ki386SaveFlags(flags);
156 Ki386DisableInterrupts();
157
158 WRITE_PORT_UCHAR((PUCHAR) TMR_CTRL, TMR_SC0 | TMR_LATCH);
159 Count = READ_PORT_UCHAR((PUCHAR) TMR_CNT0);
160 Count |= READ_PORT_UCHAR((PUCHAR) TMR_CNT0) << 8;
161
162 /* restore flags */
163 Ki386RestoreFlags(flags);
164
165 return Count;
166 }
167
168
169 VOID WaitFor8254Wraparound(VOID)
170 {
171 ULONG CurCount, PrevCount = ~0;
172 LONG Delta;
173
174 CurCount = Read8254Timer();
175
176 do
177 {
178 PrevCount = CurCount;
179 CurCount = Read8254Timer();
180 Delta = CurCount - PrevCount;
181
182 /*
183 * This limit for delta seems arbitrary, but it isn't, it's
184 * slightly above the level of error a buggy Mercury/Neptune
185 * chipset timer can cause.
186 */
187
188 }
189 while (Delta < 300);
190 }
191
192 VOID HalpCalibrateStallExecution(VOID)
193 {
194 ULONG i;
195 ULONG calib_bit;
196 ULONG CurCount;
197 PKPCR Pcr;
198 LARGE_INTEGER StartCount, EndCount;
199
200 if (UdelayCalibrated)
201 {
202 return;
203 }
204
205 UdelayCalibrated = TRUE;
206 Pcr = KeGetCurrentKPCR();
207
208 /* Initialise timer interrupt with MILLISEC ms interval */
209 WRITE_PORT_UCHAR((PUCHAR) TMR_CTRL, TMR_SC0 | TMR_BOTH | TMR_MD2); /* binary, mode 2, LSB/MSB, ch 0 */
210 WRITE_PORT_UCHAR((PUCHAR) TMR_CNT0, LATCH & 0xff); /* LSB */
211 WRITE_PORT_UCHAR((PUCHAR) TMR_CNT0, LATCH >> 8); /* MSB */
212
213 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC)
214 {
215
216 WaitFor8254Wraparound();
217 Ki386RdTSC(StartCount);
218
219 WaitFor8254Wraparound();
220 Ki386RdTSC(EndCount);
221
222 Pcr->PrcbData.MHz = (ULONG)(EndCount.QuadPart - StartCount.QuadPart) / 10000;
223 DPRINT("%dMHz\n", Pcr->PrcbData.MHz);
224 return;
225
226 }
227
228 DbgPrint("Calibrating delay loop... [");
229
230 /* Stage 1: Coarse calibration */
231
232 WaitFor8254Wraparound();
233
234 Pcr->StallScaleFactor = 1;
235
236 do
237 {
238 Pcr->StallScaleFactor <<= 1; /* Next delay count to try */
239
240 WaitFor8254Wraparound();
241
242 __KeStallExecutionProcessor(Pcr->StallScaleFactor); /* Do the delay */
243
244 CurCount = Read8254Timer();
245 }
246 while (CurCount > LATCH / 2);
247
248 Pcr->StallScaleFactor >>= 1; /* Get bottom value for delay */
249
250 /* Stage 2: Fine calibration */
251 DbgPrint("delay_count: %d", Pcr->StallScaleFactor);
252
253 calib_bit = Pcr->StallScaleFactor; /* Which bit are we going to test */
254
255 for (i = 0; i < PRECISION; i++)
256 {
257 calib_bit >>= 1; /* Next bit to calibrate */
258 if (!calib_bit)
259 {
260 break; /* If we have done all bits, stop */
261 }
262
263 Pcr->StallScaleFactor |= calib_bit; /* Set the bit in delay_count */
264
265 WaitFor8254Wraparound();
266
267 __KeStallExecutionProcessor(Pcr->StallScaleFactor); /* Do the delay */
268
269 CurCount = Read8254Timer();
270 if (CurCount <= LATCH / 2) /* If a tick has passed, turn the */
271 { /* calibrated bit back off */
272 Pcr->StallScaleFactor &= ~calib_bit;
273 }
274 }
275
276 /* We're finished: Do the finishing touches */
277
278 Pcr->StallScaleFactor /= (MILLISEC / 2); /* Calculate delay_count for 1ms */
279
280 DbgPrint("]\n");
281 DbgPrint("delay_count: %d\n", Pcr->StallScaleFactor);
282 DbgPrint("CPU speed: %d\n", Pcr->StallScaleFactor / 250);
283 #if 0
284 DbgPrint("About to start delay loop test\n");
285 DbgPrint("Waiting for five minutes...");
286 for (i = 0; i < (5*60*1000*20); i++)
287 {
288 KeStallExecutionProcessor(50);
289 }
290 DbgPrint("finished\n");
291 for(;;);
292 #endif
293 }
294
295
296 VOID STDCALL
297 HalCalibratePerformanceCounter(ULONG Count)
298 {
299 ULONG flags;
300
301 /* save flags and disable interrupts */
302 Ki386SaveFlags(flags);
303 Ki386DisableInterrupts();
304
305 __KeStallExecutionProcessor(Count);
306
307 /* restore flags */
308 Ki386RestoreFlags(flags);
309 }
310
311
312 LARGE_INTEGER STDCALL
313 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
314 /*
315 * FUNCTION: Queries the finest grained running count available in the system
316 * ARGUMENTS:
317 * PerformanceFreq (OUT) = The routine stores the number of
318 * performance counter ticks per second here
319 * RETURNS: The number of performance counter ticks since boot
320 */
321 {
322 PKPCR Pcr;
323 LARGE_INTEGER Value;
324 ULONG Flags;
325
326 Ki386SaveFlags(Flags);
327 Ki386DisableInterrupts();
328
329 Pcr = KeGetCurrentKPCR();
330
331 if (Pcr->PrcbData.FeatureBits & X86_FEATURE_TSC)
332 {
333 Ki386RestoreFlags(Flags);
334 if (NULL != PerformanceFreq)
335 {
336 PerformanceFreq->QuadPart = Pcr->PrcbData.MHz * (ULONGLONG)1000000;
337 }
338 Ki386RdTSC(Value);
339 }
340 else
341 {
342 LARGE_INTEGER TicksOld;
343 LARGE_INTEGER TicksNew;
344 ULONG CountsLeft;
345
346 Ki386RestoreFlags(Flags);
347
348 if (NULL != PerformanceFreq)
349 {
350 PerformanceFreq->QuadPart = CLOCK_TICK_RATE;
351 }
352
353 do
354 {
355 KeQueryTickCount(&TicksOld);
356 CountsLeft = Read8254Timer();
357 Value.QuadPart = TicksOld.QuadPart * LATCH + (LATCH - CountsLeft);
358 KeQueryTickCount(&TicksNew);
359 }
360 while (TicksOld.QuadPart != TicksNew.QuadPart);
361 }
362 return Value;
363 }
364
365 /* EOF */