update for HEAD-2003091401
[reactos.git] / hal / halx86 / 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
37 #define NDEBUG
38 #include <internal/debug.h>
39
40 /* GLOBALS ******************************************************************/
41
42 static unsigned int delay_count = 1;
43
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       */
48
49 #define         TMR_SC0         0       /*      Select channel 0        */
50 #define         TMR_SC1         0x40    /*      Select channel 1        */
51 #define         TMR_SC2         0x80    /*      Select channel 2        */
52
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           */
56
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                  */
63
64 #define         TMR_BCD         1       /*      BCD mode                */
65
66 #define         TMR_LATCH       0       /*      Latch command           */
67
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             */
74
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 */
80
81 static BOOLEAN UdelayCalibrated = FALSE;
82
83 /* FUNCTIONS **************************************************************/
84
85 VOID STDCALL
86 __KeStallExecutionProcessor(ULONG Loops)
87 {
88    register unsigned int i;
89    for (i=0; i<Loops;i++);
90 }
91
92 VOID STDCALL KeStallExecutionProcessor(ULONG Microseconds)
93 {
94    __KeStallExecutionProcessor((delay_count*Microseconds)/1000);
95 }
96
97 static ULONG Read8254Timer(VOID)
98 {
99   ULONG Count;
100
101   /* save flags and disable interrupts */
102   __asm__("pushf\n\t" \
103           "cli\n\t");
104
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;
108
109   /* restore flags */
110   __asm__("popf\n\t");
111
112   return Count;
113 }
114
115
116 static VOID WaitFor8254Wraparound(VOID)
117 {
118   ULONG CurCount, PrevCount = ~0;
119   LONG Delta;
120
121   CurCount = Read8254Timer();
122
123   do
124     {
125       PrevCount = CurCount;
126       CurCount = Read8254Timer();
127       Delta = CurCount - PrevCount;
128
129       /*
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.
133        */
134
135     }
136    while (Delta < 300);
137 }
138
139 VOID HalpCalibrateStallExecution(VOID)
140 {
141   ULONG i;
142   ULONG calib_bit;
143   ULONG CurCount;
144
145   if (UdelayCalibrated)
146     {
147       return;
148     }
149
150   UdelayCalibrated = TRUE;
151
152   DbgPrint("Calibrating delay loop... [");
153
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 */
158
159   /* Stage 1:  Coarse calibration                                   */
160
161   WaitFor8254Wraparound();
162
163   delay_count = 1;
164
165   do
166     {
167       delay_count <<= 1;                  /* Next delay count to try */
168
169       WaitFor8254Wraparound();
170
171       __KeStallExecutionProcessor(delay_count);      /* Do the delay */
172
173       CurCount = Read8254Timer();
174     }
175   while (CurCount > LATCH / 2);
176
177   delay_count >>= 1;              /* Get bottom value for delay     */
178
179   /* Stage 2:  Fine calibration                                     */
180   DbgPrint("delay_count: %d", delay_count);
181
182   calib_bit = delay_count;        /* Which bit are we going to test */
183
184   for (i = 0; i < PRECISION; i++)
185     {
186       calib_bit >>= 1;            /* Next bit to calibrate          */
187       if (!calib_bit)
188         {
189           break;                  /* If we have done all bits, stop */
190         }
191
192       delay_count |= calib_bit;   /* Set the bit in delay_count */
193
194       WaitFor8254Wraparound();
195
196       __KeStallExecutionProcessor(delay_count);      /* Do the delay */
197
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;
202         }
203     }
204
205   /* We're finished:  Do the finishing touches                      */
206
207   delay_count /= (MILLISEC / 2);   /* Calculate delay_count for 1ms */
208
209   DbgPrint("]\n");
210   DbgPrint("delay_count: %d\n", delay_count);
211   DbgPrint("CPU speed: %d\n", delay_count / 250);
212 #if 0
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++)
216     {
217       KeStallExecutionProcessor(50);
218     }
219   DbgPrint("finished\n");
220   for(;;);
221 #endif
222 }
223
224
225 VOID STDCALL
226 HalCalibratePerformanceCounter(ULONG Count)
227 {
228    ULONG i;
229
230    /* save flags and disable interrupts */
231    __asm__("pushf\n\t" \
232            "cli\n\t");
233
234    for (i = 0; i < Count; i++);
235
236    /* restore flags */
237    __asm__("popf\n\t");
238 }
239
240
241 LARGE_INTEGER STDCALL
242 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq)
243 /*
244  * FUNCTION: Queries the finest grained running count available in the system
245  * ARGUMENTS:
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
249  */
250 {
251   LARGE_INTEGER TicksOld;
252   LARGE_INTEGER TicksNew;
253   LARGE_INTEGER Value;
254   ULONG CountsLeft;
255
256   if (NULL != PerformanceFreq)
257     {
258       PerformanceFreq->QuadPart = CLOCK_TICK_RATE;
259     }
260
261   do
262     {
263       KeQueryTickCount(&TicksOld);
264       CountsLeft = Read8254Timer();
265       Value.QuadPart = TicksOld.QuadPart * LATCH + (LATCH - CountsLeft);
266       KeQueryTickCount(&TicksNew);
267     }
268   while (TicksOld.QuadPart != TicksNew.QuadPart);
269
270   return Value;
271 }
272
273 /* EOF */