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
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.
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.
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,
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)
33 /* INCLUDES ***************************************************************/
39 /* GLOBALS ******************************************************************/
41 #define TMR_CTRL 0x43 /* I/O for control */
42 #define TMR_CNT0 0x40 /* I/O for counter 0 */
43 #define TMR_CNT1 0x41 /* I/O for counter 1 */
44 #define TMR_CNT2 0x42 /* I/O for counter 2 */
46 #define TMR_SC0 0 /* Select channel 0 */
47 #define TMR_SC1 0x40 /* Select channel 1 */
48 #define TMR_SC2 0x80 /* Select channel 2 */
50 #define TMR_LOW 0x10 /* RW low byte only */
51 #define TMR_HIGH 0x20 /* RW high byte only */
52 #define TMR_BOTH 0x30 /* RW both bytes */
54 #define TMR_MD0 0 /* Mode 0 */
55 #define TMR_MD1 0x2 /* Mode 1 */
56 #define TMR_MD2 0x4 /* Mode 2 */
57 #define TMR_MD3 0x6 /* Mode 3 */
58 #define TMR_MD4 0x8 /* Mode 4 */
59 #define TMR_MD5 0xA /* Mode 5 */
61 #define TMR_BCD 1 /* BCD mode */
63 #define TMR_LATCH 0 /* Latch command */
65 #define TMR_READ 0xF0 /* Read command */
66 #define TMR_CNT 0x20 /* CNT bit (Active low, subtract it) */
67 #define TMR_STAT 0x10 /* Status bit (Active low, subtract it) */
68 #define TMR_CH2 0x8 /* Channel 2 bit */
69 #define TMR_CH1 0x4 /* Channel 1 bit */
70 #define TMR_CH0 0x2 /* Channel 0 bit */
72 #define MILLISEC 10 /* Number of millisec between interrupts */
73 #define HZ (1000 / MILLISEC) /* Number of interrupts per second */
74 #define CLOCK_TICK_RATE 1193182 /* Clock frequency of the timer chip */
75 #define LATCH (CLOCK_TICK_RATE / HZ) /* Count to program into the timer chip */
76 #define PRECISION 8 /* Number of bits to calibrate for delay loop */
78 static BOOLEAN UdelayCalibrated
= FALSE
;
80 /* FUNCTIONS **************************************************************/
83 * NOTE: This function MUST NOT be optimized by the compiler!
84 * If it is, it obviously will not delay AT ALL, and the system
85 * will appear completely frozen at boot since
86 * HalpCalibrateStallExecution will never return.
87 * There are three options to stop optimization:
88 * 1. Use a volatile automatic variable. Making it delay quite a bit
89 * due to memory accesses, and keeping the code portable. However,
90 * as this involves memory access it depends on both the CPU cache,
91 * e.g. if the stack used is already in a cache line or not, and
92 * whether or not we're MP. If MP, another CPU could (probably would)
93 * also access RAM at the same time - making the delay imprecise.
94 * 2. Use compiler-specific #pragma's to disable optimization.
95 * 3. Use inline assembly, making it equally unportable as #2.
96 * For supported compilers we use inline assembler. For the others,
99 VOID STDCALL
__attribute__((noinline
))
100 __KeStallExecutionProcessor(ULONG Loops
)
106 #if defined(__GNUC__)
107 __asm__
__volatile__ (
110 "jnz ROSL1" : : "d" (Loops
));
112 #elif defined(_MSC_VER)
118 volatile unsigned int target
= Loops
;
120 for (i
=0; i
<target
;i
++);
124 VOID STDCALL
KeStallExecutionProcessor(ULONG Microseconds
)
126 PKIPCR Pcr
= (PKIPCR
)KeGetCurrentKPCR();
128 if (Pcr
->PrcbData
.FeatureBits
& X86_FEATURE_TSC
)
130 LARGE_INTEGER EndCount
, CurrentCount
;
131 Ki386RdTSC(EndCount
);
132 EndCount
.QuadPart
+= Microseconds
* (ULONGLONG
)Pcr
->PrcbData
.MHz
;
135 Ki386RdTSC(CurrentCount
);
137 while (CurrentCount
.QuadPart
< EndCount
.QuadPart
);
141 __KeStallExecutionProcessor((Pcr
->StallScaleFactor
*Microseconds
)/1000);
145 static ULONG
Read8254Timer(VOID
)
150 /* save flags and disable interrupts */
151 Ki386SaveFlags(flags
);
152 Ki386DisableInterrupts();
154 WRITE_PORT_UCHAR((PUCHAR
) TMR_CTRL
, TMR_SC0
| TMR_LATCH
);
155 Count
= READ_PORT_UCHAR((PUCHAR
) TMR_CNT0
);
156 Count
|= READ_PORT_UCHAR((PUCHAR
) TMR_CNT0
) << 8;
159 Ki386RestoreFlags(flags
);
165 VOID
WaitFor8254Wraparound(VOID
)
167 ULONG CurCount
, PrevCount
= ~0;
170 CurCount
= Read8254Timer();
174 PrevCount
= CurCount
;
175 CurCount
= Read8254Timer();
176 Delta
= CurCount
- PrevCount
;
179 * This limit for delta seems arbitrary, but it isn't, it's
180 * slightly above the level of error a buggy Mercury/Neptune
181 * chipset timer can cause.
188 VOID
HalpCalibrateStallExecution(VOID
)
194 LARGE_INTEGER StartCount
, EndCount
;
196 if (UdelayCalibrated
)
201 UdelayCalibrated
= TRUE
;
202 Pcr
= (PKIPCR
)KeGetCurrentKPCR();
204 /* Initialise timer interrupt with MILLISEC ms interval */
205 WRITE_PORT_UCHAR((PUCHAR
) TMR_CTRL
, TMR_SC0
| TMR_BOTH
| TMR_MD2
); /* binary, mode 2, LSB/MSB, ch 0 */
206 WRITE_PORT_UCHAR((PUCHAR
) TMR_CNT0
, LATCH
& 0xff); /* LSB */
207 WRITE_PORT_UCHAR((PUCHAR
) TMR_CNT0
, LATCH
>> 8); /* MSB */
209 if (Pcr
->PrcbData
.FeatureBits
& X86_FEATURE_TSC
)
212 WaitFor8254Wraparound();
213 Ki386RdTSC(StartCount
);
215 WaitFor8254Wraparound();
216 Ki386RdTSC(EndCount
);
218 Pcr
->PrcbData
.MHz
= (ULONG
)(EndCount
.QuadPart
- StartCount
.QuadPart
) / 10000;
219 DPRINT("%luMHz\n", Pcr
->PrcbData
.MHz
);
224 DbgPrint("Calibrating delay loop... [");
226 /* Stage 1: Coarse calibration */
228 WaitFor8254Wraparound();
230 Pcr
->StallScaleFactor
= 1;
234 Pcr
->StallScaleFactor
<<= 1; /* Next delay count to try */
236 WaitFor8254Wraparound();
238 __KeStallExecutionProcessor(Pcr
->StallScaleFactor
); /* Do the delay */
240 CurCount
= Read8254Timer();
242 while (CurCount
> LATCH
/ 2);
244 Pcr
->StallScaleFactor
>>= 1; /* Get bottom value for delay */
246 /* Stage 2: Fine calibration */
247 DbgPrint("delay_count: %d", Pcr
->StallScaleFactor
);
249 calib_bit
= Pcr
->StallScaleFactor
; /* Which bit are we going to test */
251 for (i
= 0; i
< PRECISION
; i
++)
253 calib_bit
>>= 1; /* Next bit to calibrate */
256 break; /* If we have done all bits, stop */
259 Pcr
->StallScaleFactor
|= calib_bit
; /* Set the bit in delay_count */
261 WaitFor8254Wraparound();
263 __KeStallExecutionProcessor(Pcr
->StallScaleFactor
); /* Do the delay */
265 CurCount
= Read8254Timer();
266 if (CurCount
<= LATCH
/ 2) /* If a tick has passed, turn the */
267 { /* calibrated bit back off */
268 Pcr
->StallScaleFactor
&= ~calib_bit
;
272 /* We're finished: Do the finishing touches */
274 Pcr
->StallScaleFactor
/= (MILLISEC
/ 2); /* Calculate delay_count for 1ms */
277 DbgPrint("delay_count: %d\n", Pcr
->StallScaleFactor
);
278 DbgPrint("CPU speed: %d\n", Pcr
->StallScaleFactor
/ 250);
280 DbgPrint("About to start delay loop test\n");
281 DbgPrint("Waiting for five minutes...");
282 for (i
= 0; i
< (5*60*1000*20); i
++)
284 KeStallExecutionProcessor(50);
286 DbgPrint("finished\n");
293 HalCalibratePerformanceCounter(ULONG Count
)
297 /* save flags and disable interrupts */
298 Ki386SaveFlags(flags
);
299 Ki386DisableInterrupts();
301 __KeStallExecutionProcessor(Count
);
304 Ki386RestoreFlags(flags
);
308 LARGE_INTEGER STDCALL
309 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
311 * FUNCTION: Queries the finest grained running count available in the system
313 * PerformanceFreq (OUT) = The routine stores the number of
314 * performance counter ticks per second here
315 * RETURNS: The number of performance counter ticks since boot
322 Ki386SaveFlags(Flags
);
323 Ki386DisableInterrupts();
325 Pcr
= (PKIPCR
)KeGetCurrentKPCR();
327 if (Pcr
->PrcbData
.FeatureBits
& X86_FEATURE_TSC
)
329 Ki386RestoreFlags(Flags
);
330 if (NULL
!= PerformanceFreq
)
332 PerformanceFreq
->QuadPart
= Pcr
->PrcbData
.MHz
* (ULONGLONG
)1000000;
338 LARGE_INTEGER TicksOld
;
339 LARGE_INTEGER TicksNew
;
342 Ki386RestoreFlags(Flags
);
344 if (NULL
!= PerformanceFreq
)
346 PerformanceFreq
->QuadPart
= CLOCK_TICK_RATE
;
351 KeQueryTickCount(&TicksOld
);
352 CountsLeft
= Read8254Timer();
353 Value
.QuadPart
= TicksOld
.QuadPart
* LATCH
+ (LATCH
- CountsLeft
);
354 KeQueryTickCount(&TicksNew
);
356 while (TicksOld
.QuadPart
!= TicksNew
.QuadPart
);