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