* Patch by Daniel Engström, 13 Nov 2002:
[platform/kernel/u-boot.git] / cpu / i386 / timer.c
1 /*
2  * (C) Copyright 2002
3  * Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <asm/io.h>
26 #include <asm/i8254.h>
27 #include <asm/ibmpc.h>
28
29
30 static volatile unsigned long system_ticks;
31 static int timer_init_done =0;
32
33 static void timer_isr(void *unused)
34 {
35         system_ticks++;
36 }
37
38 unsigned long get_system_ticks(void)
39 {
40         return system_ticks;
41 }
42
43 #define TIMER0_VALUE 0x04aa /* 1kHz 1.9318MHz / 1000 */
44 #define TIMER2_VALUE 0x0a8e /* 440Hz */
45
46 int timer_init(void)
47 {
48         system_ticks = 0;
49         
50         irq_install_handler(0, timer_isr, NULL);
51         
52         /* initialize timer 0 and 2 
53          * 
54          * Timer 0 is used to increment system_tick 1000 times/sec
55          * Timer 1 was used for DRAM refresh in early PC's
56          * Timer 2 is used to drive the speaker
57          * (to stasrt a beep: write 3 to port 0x61,
58          * to stop it again: write 0)
59          */
60                 
61         outb(PIT_CMD_CTR0|PIT_CMD_BOTH|PIT_CMD_MODE2, PIT_BASE + PIT_COMMAND);
62         outb(TIMER0_VALUE&0xff, PIT_BASE + PIT_T0);
63         outb(TIMER0_VALUE>>8, PIT_BASE + PIT_T0);
64
65         outb(PIT_CMD_CTR2|PIT_CMD_BOTH|PIT_CMD_MODE3, PIT_BASE + PIT_COMMAND);
66         outb(TIMER2_VALUE&0xff, PIT_BASE + PIT_T2);
67         outb(TIMER2_VALUE>>8, PIT_BASE + PIT_T2);
68
69         timer_init_done = 1;
70         
71         return 0;
72 }
73
74
75 #ifdef CFG_TIMER_GENERIC
76
77 /* the unit for these is CFG_HZ */
78
79 /* FixMe: implement these */
80 void reset_timer (void)
81 {
82         system_ticks = 0;
83 }
84
85 ulong get_timer (ulong base)
86 {
87         return (system_ticks - base);
88 }
89
90 void set_timer (ulong t)        
91 {
92         system_ticks = t;
93 }
94
95 static u16 read_pit(void)
96 {
97         u8 low;
98         outb(PIT_CMD_LATCH, PIT_BASE + PIT_COMMAND);
99         low = inb(PIT_BASE + PIT_T0);
100         return ((inb(PIT_BASE + PIT_T0) << 8) | low);
101 }
102
103 /* this is not very exact */
104 void udelay (unsigned long usec)
105 {       
106         int counter;
107         int wraps;
108         
109         if (!timer_init_done) {
110                 return;
111         }
112         counter = read_pit();
113         wraps = usec/1000;
114         usec = usec%1000;
115         
116         usec*=1194;
117         usec/=1000;
118         usec+=counter; 
119         if (usec > 1194) {
120                 usec-=1194;
121                 wraps++;
122         }
123
124         while (1) {
125                 int new_count = read_pit();
126                 
127                 if (((new_count < usec) && !wraps) || wraps < 0) {
128                         break;
129                 }
130                 
131                 if (new_count > counter) {
132                         wraps--;
133                 }
134                 counter = new_count;
135         }
136         
137 }
138
139 #if 0
140 /* this is a version with debug output */
141 void _udelay (unsigned long usec)
142 {       
143         int counter;
144         int wraps;
145         
146         int usec1, usec2, usec3;
147         int wraps1, wraps2, wraps3, wraps4;
148         int ctr1, ctr2, ctr3, nct1, nct2;
149         int i;
150         usec1=usec;
151         if (!timer_init_done) {
152                 return;
153         }
154         counter = read_pit();
155         ctr1 = counter;
156         wraps = usec/1000;
157         usec = usec%1000;
158         
159         usec2 = usec;
160         wraps1 = wraps;
161         
162         usec*=1194;
163         usec/=1000;
164         usec+=counter; 
165         if (usec > 1194) {
166                 usec-=1194;
167                 wraps++;
168         }
169
170         usec3 = usec;
171         wraps2 = wraps;
172         
173         ctr2 = wraps3 = nct1 = 4711;
174         ctr3 = wraps4 = nct2 = 4711;
175         i=0;
176         while (1) {
177                 int new_count = read_pit();
178                 i++;
179                 if ((new_count < usec && !wraps) || wraps < 0) {
180                         break;
181                 }
182                 
183                 if (new_count > counter) {
184                         wraps--;
185                 }
186                 if (ctr2==4711) {
187                         ctr2 = counter;
188                         wraps3 = wraps;
189                         nct1 = new_count;
190                 } else {
191                         ctr3 = counter;
192                         wraps4 = wraps;
193                         nct2 = new_count;
194                 }
195                 
196                 counter = new_count;
197         }
198         
199         printf("udelay(%d)\n", usec1);
200         printf("counter %d\n", ctr1);
201         printf("1: wraps %d, usec %d\n", wraps1, usec2);
202         printf("2: wraps %d, usec %d\n", wraps2, usec3);
203         printf("new_count[0] %d counter %d wraps %d\n", nct1, ctr2, wraps3);
204         printf("new_count[%d] %d counter %d wraps %d\n", i, nct2, ctr3, wraps4);
205
206         printf("%d %d %d %d %d\n",
207                read_pit(), read_pit(), read_pit(),
208                read_pit(), read_pit());
209 }
210 #endif
211 #endif