:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / hal / halx86 / udelay.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 MILLISEC     (10)
45 #define FREQ         (1000/MILLISEC)
46
47 #define PRECISION    (8)
48
49 #define         TMR_CTRL        0x43    /*      I/O for control         */
50 #define         TMR_CNT0        0x40    /*      I/O for counter 0       */
51 #define         TMR_CNT1        0x41    /*      I/O for counter 1       */
52 #define         TMR_CNT2        0x42    /*      I/O for counter 2       */
53
54 #define         TMR_SC0         0       /*      Select channel 0        */
55 #define         TMR_SC1         0x40    /*      Select channel 1        */
56 #define         TMR_SC2         0x80    /*      Select channel 2        */
57
58 #define         TMR_LOW         0x10    /*      RW low byte only        */
59 #define         TMR_HIGH        0x20    /*      RW high byte only       */
60 #define         TMR_BOTH        0x30    /*      RW both bytes           */
61
62 #define         TMR_MD0         0       /*      Mode 0                  */
63 #define         TMR_MD1         0x2     /*      Mode 1                  */
64 #define         TMR_MD2         0x4     /*      Mode 2                  */
65 #define         TMR_MD3         0x6     /*      Mode 3                  */
66 #define         TMR_MD4         0x8     /*      Mode 4                  */
67 #define         TMR_MD5         0xA     /*      Mode 5                  */
68
69 #define         TMR_BCD         1       /*      BCD mode                */
70
71 #define         TMR_LATCH       0       /*      Latch command           */
72
73 #define         TMR_READ        0xF0    /*    Read command              */
74 #define         TMR_CNT         0x20    /*    CNT bit  (Active low, subtract it) */
75 #define         TMR_STAT        0x10    /*    Status bit  (Active low, subtract it) */
76 #define         TMR_CH2         0x8     /*    Channel 2 bit             */
77 #define         TMR_CH1         0x4     /*    Channel 1 bit             */
78 #define         TMR_CH0         0x2     /*    Channel 0 bit             */
79
80 static BOOLEAN UdelayCalibrated = FALSE;
81
82 /* FUNCTIONS **************************************************************/
83
84 void init_pit(float h, unsigned char channel)
85 {
86         unsigned int temp=0;
87         
88         temp = 1193180/h;
89         
90 //      WRITE_PORT_UCHAR((PUCHAR)TMR_CTRL, 
91 //                       (channel*0x40) + TMR_BOTH + TMR_MD3);
92         WRITE_PORT_UCHAR((PUCHAR)TMR_CTRL,
93                          (channel*0x40) + TMR_BOTH + TMR_MD2);
94         WRITE_PORT_UCHAR((PUCHAR)(0x40+channel), 
95                          (unsigned char) temp);
96         WRITE_PORT_UCHAR((PUCHAR)(0x40+channel), 
97                          (unsigned char) (temp>>8));
98 }
99
100 VOID STDCALL
101 __KeStallExecutionProcessor(ULONG Loops)
102 {
103    register unsigned int i;
104    for (i=0; i<Loops;i++);
105 }
106
107 VOID STDCALL KeStallExecutionProcessor(ULONG Microseconds)
108 {
109    __KeStallExecutionProcessor((delay_count*Microseconds)/1000);
110 }
111
112 #define HZ (100)
113 #define CLOCK_TICK_RATE (1193182)
114 #define LATCH (CLOCK_TICK_RATE / HZ)
115
116 static ULONG Read8254Timer(VOID)
117 {
118         ULONG Count;
119
120         WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
121         Count = READ_PORT_UCHAR((PUCHAR)0x40);
122         Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
123         return Count;
124 }
125
126
127 static VOID WaitFor8254Wraparound(VOID)
128 {
129         ULONG CurCount, PrevCount = ~0;
130         LONG Delta;
131
132         CurCount = Read8254Timer();
133
134         do {
135                 PrevCount = CurCount;
136                 CurCount = Read8254Timer();
137                 Delta = CurCount - PrevCount;
138
139         /*
140          * This limit for delta seems arbitrary, but it isn't, it's
141          * slightly above the level of error a buggy Mercury/Neptune
142          * chipset timer can cause.
143          */
144
145         } while (Delta < 300);
146 }
147
148 VOID HalpCalibrateStallExecution(VOID)
149 {
150    ULONG i;
151    ULONG calib_bit;
152          ULONG CurCount;
153
154    if (UdelayCalibrated)
155       return;
156
157    UdelayCalibrated = TRUE;
158
159    DbgPrint("Calibrating delay loop... [");
160
161    /* Initialise timer interrupt with MILLISECOND ms interval        */
162    WRITE_PORT_UCHAR((PUCHAR)0x43, 0x34);  /* binary, mode 2, LSB/MSB, ch 0 */
163    WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH & 0xff); /* LSB */
164    WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH >> 8); /* MSB */
165
166    /* Stage 1:  Coarse calibration                                   */
167
168    WaitFor8254Wraparound();
169
170    delay_count = 1;
171
172    do {
173       delay_count <<= 1;                  /* Next delay count to try */
174
175       WaitFor8254Wraparound();
176
177       __KeStallExecutionProcessor(delay_count);      /* Do the delay */
178
179             CurCount = Read8254Timer();
180    } while (CurCount > LATCH / 2);
181
182    delay_count >>= 1;              /* Get bottom value for delay     */
183
184    /* Stage 2:  Fine calibration                                     */
185    DbgPrint("delay_count: %d", delay_count);
186
187    calib_bit = delay_count;        /* Which bit are we going to test */
188
189    for(i=0;i<PRECISION;i++) {
190       calib_bit >>= 1;             /* Next bit to calibrate          */
191       if(!calib_bit) break;        /* If we have done all bits, stop */
192
193       delay_count |= calib_bit;        /* Set the bit in delay_count */
194
195       WaitFor8254Wraparound();
196
197       __KeStallExecutionProcessor(delay_count);      /* Do the delay */
198
199       CurCount = Read8254Timer();
200       if (CurCount <= LATCH / 2)   /* If a tick has passed, turn the */
201         delay_count &= ~calib_bit; /* calibrated bit back off        */
202    }
203
204    /* We're finished:  Do the finishing touches                      */
205
206    delay_count /= (MILLISEC / 2);   /* Calculate delay_count for 1ms */
207
208    DbgPrint("]\n");
209    DbgPrint("delay_count: %d\n", delay_count);
210    DbgPrint("CPU speed: %d\n", delay_count/250);
211 #if 0
212    DbgPrint("About to start delay loop test\n");
213    DbgPrint("Waiting for five minutes...");
214    for (i = 0; i < (5*60*1000*20); i++)
215      {
216         KeStallExecutionProcessor(50);
217      }
218    DbgPrint("finished\n");
219    for(;;);
220 #endif
221 }
222
223 /* EOF */