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 ***************************************************************/
35 #include <ddk/ntddk.h>
38 #include <internal/debug.h>
40 /* GLOBALS ******************************************************************/
42 static unsigned int delay_count = 1;
44 #define TMR_CTRL 0x43 /* I/O for control */
45 #define TMR_CNT0 0x40 /* I/O for counter 0 */
46 #define TMR_CNT1 0x41 /* I/O for counter 1 */
47 #define TMR_CNT2 0x42 /* I/O for counter 2 */
49 #define TMR_SC0 0 /* Select channel 0 */
50 #define TMR_SC1 0x40 /* Select channel 1 */
51 #define TMR_SC2 0x80 /* Select channel 2 */
53 #define TMR_LOW 0x10 /* RW low byte only */
54 #define TMR_HIGH 0x20 /* RW high byte only */
55 #define TMR_BOTH 0x30 /* RW both bytes */
57 #define TMR_MD0 0 /* Mode 0 */
58 #define TMR_MD1 0x2 /* Mode 1 */
59 #define TMR_MD2 0x4 /* Mode 2 */
60 #define TMR_MD3 0x6 /* Mode 3 */
61 #define TMR_MD4 0x8 /* Mode 4 */
62 #define TMR_MD5 0xA /* Mode 5 */
64 #define TMR_BCD 1 /* BCD mode */
66 #define TMR_LATCH 0 /* Latch command */
68 #define TMR_READ 0xF0 /* Read command */
69 #define TMR_CNT 0x20 /* CNT bit (Active low, subtract it) */
70 #define TMR_STAT 0x10 /* Status bit (Active low, subtract it) */
71 #define TMR_CH2 0x8 /* Channel 2 bit */
72 #define TMR_CH1 0x4 /* Channel 1 bit */
73 #define TMR_CH0 0x2 /* Channel 0 bit */
75 #define MILLISEC 10 /* Number of millisec between interrupts */
76 #define HZ (1000 / MILLISEC) /* Number of interrupts per second */
77 #define CLOCK_TICK_RATE 1193182 /* Clock frequency of the timer chip */
78 #define LATCH (CLOCK_TICK_RATE / HZ) /* Count to program into the timer chip */
79 #define PRECISION 8 /* Number of bits to calibrate for delay loop */
81 static BOOLEAN UdelayCalibrated = FALSE;
83 /* FUNCTIONS **************************************************************/
86 __KeStallExecutionProcessor(ULONG Loops)
88 register unsigned int i;
89 for (i=0; i<Loops;i++);
92 VOID STDCALL KeStallExecutionProcessor(ULONG Microseconds)
94 __KeStallExecutionProcessor((delay_count*Microseconds)/1000);
97 static ULONG Read8254Timer(VOID)
101 /* save flags and disable interrupts */
102 __asm__("pushf\n\t" \
105 WRITE_PORT_UCHAR((PUCHAR) TMR_CTRL, TMR_SC0 | TMR_LATCH);
106 Count = READ_PORT_UCHAR((PUCHAR) TMR_CNT0);
107 Count |= READ_PORT_UCHAR((PUCHAR) TMR_CNT0) << 8;
116 static VOID WaitFor8254Wraparound(VOID)
118 ULONG CurCount, PrevCount = ~0;
121 CurCount = Read8254Timer();
125 PrevCount = CurCount;
126 CurCount = Read8254Timer();
127 Delta = CurCount - PrevCount;
130 * This limit for delta seems arbitrary, but it isn't, it's
131 * slightly above the level of error a buggy Mercury/Neptune
132 * chipset timer can cause.
139 VOID HalpCalibrateStallExecution(VOID)
145 if (UdelayCalibrated)
150 UdelayCalibrated = TRUE;
152 DbgPrint("Calibrating delay loop... [");
154 /* Initialise timer interrupt with MILLISEC ms interval */
155 WRITE_PORT_UCHAR((PUCHAR) TMR_CTRL, TMR_SC0 | TMR_BOTH | TMR_MD2); /* binary, mode 2, LSB/MSB, ch 0 */
156 WRITE_PORT_UCHAR((PUCHAR) TMR_CNT0, LATCH & 0xff); /* LSB */
157 WRITE_PORT_UCHAR((PUCHAR) TMR_CNT0, LATCH >> 8); /* MSB */
159 /* Stage 1: Coarse calibration */
161 WaitFor8254Wraparound();
167 delay_count <<= 1; /* Next delay count to try */
169 WaitFor8254Wraparound();
171 __KeStallExecutionProcessor(delay_count); /* Do the delay */
173 CurCount = Read8254Timer();
175 while (CurCount > LATCH / 2);
177 delay_count >>= 1; /* Get bottom value for delay */
179 /* Stage 2: Fine calibration */
180 DbgPrint("delay_count: %d", delay_count);
182 calib_bit = delay_count; /* Which bit are we going to test */
184 for (i = 0; i < PRECISION; i++)
186 calib_bit >>= 1; /* Next bit to calibrate */
189 break; /* If we have done all bits, stop */
192 delay_count |= calib_bit; /* Set the bit in delay_count */
194 WaitFor8254Wraparound();
196 __KeStallExecutionProcessor(delay_count); /* Do the delay */
198 CurCount = Read8254Timer();
199 if (CurCount <= LATCH / 2) /* If a tick has passed, turn the */
200 { /* calibrated bit back off */
201 delay_count &= ~calib_bit;
205 /* We're finished: Do the finishing touches */
207 delay_count /= (MILLISEC / 2); /* Calculate delay_count for 1ms */
210 DbgPrint("delay_count: %d\n", delay_count);
211 DbgPrint("CPU speed: %d\n", delay_count / 250);
213 DbgPrint("About to start delay loop test\n");
214 DbgPrint("Waiting for five minutes...");
215 for (i = 0; i < (5*60*1000*20); i++)
217 KeStallExecutionProcessor(50);
219 DbgPrint("finished\n");
226 HalCalibratePerformanceCounter(ULONG Count)
230 /* save flags and disable interrupts */
231 __asm__("pushf\n\t" \
234 for (i = 0; i < Count; i++);
241 LARGE_INTEGER STDCALL
242 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
244 * FUNCTION: Queries the finest grained running count available in the system
246 * PerformanceFreq (OUT) = The routine stores the number of
247 * performance counter ticks per second here
248 * RETURNS: The number of performance counter ticks since boot
251 LARGE_INTEGER TicksOld;
252 LARGE_INTEGER TicksNew;
256 if (NULL != PerformanceFreq)
258 PerformanceFreq->QuadPart = CLOCK_TICK_RATE;
263 KeQueryTickCount(&TicksOld);
264 CountsLeft = Read8254Timer();
265 Value.QuadPart = TicksOld.QuadPart * LATCH + (LATCH - CountsLeft);
266 KeQueryTickCount(&TicksNew);
268 while (TicksOld.QuadPart != TicksNew.QuadPart);