xref: /titanic_52/usr/src/grub/grub-0.97/netboot/i386_timer.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1*1b8adde7SWilliam Kucharski /* A couple of routines to implement a low-overhead timer for drivers */
2*1b8adde7SWilliam Kucharski 
3*1b8adde7SWilliam Kucharski  /*
4*1b8adde7SWilliam Kucharski  * This program is free software; you can redistribute it and/or
5*1b8adde7SWilliam Kucharski  * modify it under the terms of the GNU General Public License as
6*1b8adde7SWilliam Kucharski  * published by the Free Software Foundation; either version 2, or (at
7*1b8adde7SWilliam Kucharski  * your option) any later version.
8*1b8adde7SWilliam Kucharski  */
9*1b8adde7SWilliam Kucharski #include "grub.h"
10*1b8adde7SWilliam Kucharski #include "osdep.h"
11*1b8adde7SWilliam Kucharski #include "io.h"
12*1b8adde7SWilliam Kucharski #include "timer.h"
13*1b8adde7SWilliam Kucharski #include "latch.h"
14*1b8adde7SWilliam Kucharski 
15*1b8adde7SWilliam Kucharski void __load_timer2(unsigned int ticks)
16*1b8adde7SWilliam Kucharski {
17*1b8adde7SWilliam Kucharski 	/*
18*1b8adde7SWilliam Kucharski 	 * Now let's take care of PPC channel 2
19*1b8adde7SWilliam Kucharski 	 *
20*1b8adde7SWilliam Kucharski 	 * Set the Gate high, program PPC channel 2 for mode 0,
21*1b8adde7SWilliam Kucharski 	 * (interrupt on terminal count mode), binary count,
22*1b8adde7SWilliam Kucharski 	 * load 5 * LATCH count, (LSB and MSB) to begin countdown.
23*1b8adde7SWilliam Kucharski 	 *
24*1b8adde7SWilliam Kucharski 	 * Note some implementations have a bug where the high bits byte
25*1b8adde7SWilliam Kucharski 	 * of channel 2 is ignored.
26*1b8adde7SWilliam Kucharski 	 */
27*1b8adde7SWilliam Kucharski 	/* Set up the timer gate, turn off the speaker */
28*1b8adde7SWilliam Kucharski 	/* Set the Gate high, disable speaker */
29*1b8adde7SWilliam Kucharski 	outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
30*1b8adde7SWilliam Kucharski 	/* binary, mode 0, LSB/MSB, Ch 2 */
31*1b8adde7SWilliam Kucharski 	outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
32*1b8adde7SWilliam Kucharski 	/* LSB of ticks */
33*1b8adde7SWilliam Kucharski 	outb(ticks & 0xFF, TIMER2_PORT);
34*1b8adde7SWilliam Kucharski 	/* MSB of ticks */
35*1b8adde7SWilliam Kucharski 	outb(ticks >> 8, TIMER2_PORT);
36*1b8adde7SWilliam Kucharski }
37*1b8adde7SWilliam Kucharski 
38*1b8adde7SWilliam Kucharski static int __timer2_running(void)
39*1b8adde7SWilliam Kucharski {
40*1b8adde7SWilliam Kucharski 	return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
41*1b8adde7SWilliam Kucharski }
42*1b8adde7SWilliam Kucharski 
43*1b8adde7SWilliam Kucharski #if !defined(CONFIG_TSC_CURRTICKS)
44*1b8adde7SWilliam Kucharski void setup_timers(void)
45*1b8adde7SWilliam Kucharski {
46*1b8adde7SWilliam Kucharski 	return;
47*1b8adde7SWilliam Kucharski }
48*1b8adde7SWilliam Kucharski 
49*1b8adde7SWilliam Kucharski void load_timer2(unsigned int ticks)
50*1b8adde7SWilliam Kucharski {
51*1b8adde7SWilliam Kucharski 	return __load_timer2(ticks);
52*1b8adde7SWilliam Kucharski }
53*1b8adde7SWilliam Kucharski 
54*1b8adde7SWilliam Kucharski int timer2_running(void)
55*1b8adde7SWilliam Kucharski {
56*1b8adde7SWilliam Kucharski 	return __timer2_running();
57*1b8adde7SWilliam Kucharski }
58*1b8adde7SWilliam Kucharski 
59*1b8adde7SWilliam Kucharski void ndelay(unsigned int nsecs)
60*1b8adde7SWilliam Kucharski {
61*1b8adde7SWilliam Kucharski 	waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000);
62*1b8adde7SWilliam Kucharski }
63*1b8adde7SWilliam Kucharski void udelay(unsigned int usecs)
64*1b8adde7SWilliam Kucharski {
65*1b8adde7SWilliam Kucharski 	waiton_timer2((usecs * TICKS_PER_MS)/1000);
66*1b8adde7SWilliam Kucharski }
67*1b8adde7SWilliam Kucharski #endif /* !defined(CONFIG_TSC_CURRTICKS) */
68*1b8adde7SWilliam Kucharski 
69*1b8adde7SWilliam Kucharski #if defined(CONFIG_TSC_CURRTICKS)
70*1b8adde7SWilliam Kucharski 
71*1b8adde7SWilliam Kucharski #define rdtsc(low,high) \
72*1b8adde7SWilliam Kucharski      __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
73*1b8adde7SWilliam Kucharski 
74*1b8adde7SWilliam Kucharski #define rdtscll(val) \
75*1b8adde7SWilliam Kucharski      __asm__ __volatile__ ("rdtsc" : "=A" (val))
76*1b8adde7SWilliam Kucharski 
77*1b8adde7SWilliam Kucharski 
78*1b8adde7SWilliam Kucharski /* Number of clock ticks to time with the rtc */
79*1b8adde7SWilliam Kucharski #define LATCH 0xFF
80*1b8adde7SWilliam Kucharski 
81*1b8adde7SWilliam Kucharski #define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH)
82*1b8adde7SWilliam Kucharski #define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC)
83*1b8adde7SWilliam Kucharski 
84*1b8adde7SWilliam Kucharski static void sleep_latch(void)
85*1b8adde7SWilliam Kucharski {
86*1b8adde7SWilliam Kucharski 	__load_timer2(LATCH);
87*1b8adde7SWilliam Kucharski 	while(__timer2_running());
88*1b8adde7SWilliam Kucharski }
89*1b8adde7SWilliam Kucharski 
90*1b8adde7SWilliam Kucharski /* ------ Calibrate the TSC -------
91*1b8adde7SWilliam Kucharski  * Time how long it takes to excute a loop that runs in known time.
92*1b8adde7SWilliam Kucharski  * And find the convertion needed to get to CLOCK_TICK_RATE
93*1b8adde7SWilliam Kucharski  */
94*1b8adde7SWilliam Kucharski 
95*1b8adde7SWilliam Kucharski 
96*1b8adde7SWilliam Kucharski static unsigned long long calibrate_tsc(void)
97*1b8adde7SWilliam Kucharski {
98*1b8adde7SWilliam Kucharski 	unsigned long startlow, starthigh;
99*1b8adde7SWilliam Kucharski 	unsigned long endlow, endhigh;
100*1b8adde7SWilliam Kucharski 
101*1b8adde7SWilliam Kucharski 	rdtsc(startlow,starthigh);
102*1b8adde7SWilliam Kucharski 	sleep_latch();
103*1b8adde7SWilliam Kucharski 	rdtsc(endlow,endhigh);
104*1b8adde7SWilliam Kucharski 
105*1b8adde7SWilliam Kucharski 	/* 64-bit subtract - gcc just messes up with long longs */
106*1b8adde7SWilliam Kucharski 	__asm__("subl %2,%0\n\t"
107*1b8adde7SWilliam Kucharski 		"sbbl %3,%1"
108*1b8adde7SWilliam Kucharski 		:"=a" (endlow), "=d" (endhigh)
109*1b8adde7SWilliam Kucharski 		:"g" (startlow), "g" (starthigh),
110*1b8adde7SWilliam Kucharski 		"0" (endlow), "1" (endhigh));
111*1b8adde7SWilliam Kucharski 
112*1b8adde7SWilliam Kucharski 	/* Error: ECPUTOOFAST */
113*1b8adde7SWilliam Kucharski 	if (endhigh)
114*1b8adde7SWilliam Kucharski 		goto bad_ctc;
115*1b8adde7SWilliam Kucharski 
116*1b8adde7SWilliam Kucharski 	endlow *= TICKS_PER_LATCH;
117*1b8adde7SWilliam Kucharski 	return endlow;
118*1b8adde7SWilliam Kucharski 
119*1b8adde7SWilliam Kucharski 	/*
120*1b8adde7SWilliam Kucharski 	 * The CTC wasn't reliable: we got a hit on the very first read,
121*1b8adde7SWilliam Kucharski 	 * or the CPU was so fast/slow that the quotient wouldn't fit in
122*1b8adde7SWilliam Kucharski 	 * 32 bits..
123*1b8adde7SWilliam Kucharski 	 */
124*1b8adde7SWilliam Kucharski bad_ctc:
125*1b8adde7SWilliam Kucharski 	printf("bad_ctc\n");
126*1b8adde7SWilliam Kucharski 	return 0;
127*1b8adde7SWilliam Kucharski }
128*1b8adde7SWilliam Kucharski 
129*1b8adde7SWilliam Kucharski static unsigned long clocks_per_tick;
130*1b8adde7SWilliam Kucharski void setup_timers(void)
131*1b8adde7SWilliam Kucharski {
132*1b8adde7SWilliam Kucharski 	if (!clocks_per_tick) {
133*1b8adde7SWilliam Kucharski 		clocks_per_tick = calibrate_tsc();
134*1b8adde7SWilliam Kucharski 		/* Display the CPU Mhz to easily test if the calibration was bad */
135*1b8adde7SWilliam Kucharski 		printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
136*1b8adde7SWilliam Kucharski 	}
137*1b8adde7SWilliam Kucharski }
138*1b8adde7SWilliam Kucharski 
139*1b8adde7SWilliam Kucharski unsigned long currticks(void)
140*1b8adde7SWilliam Kucharski {
141*1b8adde7SWilliam Kucharski 	unsigned long clocks_high, clocks_low;
142*1b8adde7SWilliam Kucharski 	unsigned long currticks;
143*1b8adde7SWilliam Kucharski 	/* Read the Time Stamp Counter */
144*1b8adde7SWilliam Kucharski 	rdtsc(clocks_low, clocks_high);
145*1b8adde7SWilliam Kucharski 
146*1b8adde7SWilliam Kucharski 	/* currticks = clocks / clocks_per_tick; */
147*1b8adde7SWilliam Kucharski 	__asm__("divl %1"
148*1b8adde7SWilliam Kucharski 		:"=a" (currticks)
149*1b8adde7SWilliam Kucharski 		:"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high));
150*1b8adde7SWilliam Kucharski 
151*1b8adde7SWilliam Kucharski 
152*1b8adde7SWilliam Kucharski 	return currticks;
153*1b8adde7SWilliam Kucharski }
154*1b8adde7SWilliam Kucharski 
155*1b8adde7SWilliam Kucharski static unsigned long long timer_timeout;
156*1b8adde7SWilliam Kucharski static int __timer_running(void)
157*1b8adde7SWilliam Kucharski {
158*1b8adde7SWilliam Kucharski 	unsigned long long now;
159*1b8adde7SWilliam Kucharski 	rdtscll(now);
160*1b8adde7SWilliam Kucharski 	return now < timer_timeout;
161*1b8adde7SWilliam Kucharski }
162*1b8adde7SWilliam Kucharski 
163*1b8adde7SWilliam Kucharski void udelay(unsigned int usecs)
164*1b8adde7SWilliam Kucharski {
165*1b8adde7SWilliam Kucharski 	unsigned long long now;
166*1b8adde7SWilliam Kucharski 	rdtscll(now);
167*1b8adde7SWilliam Kucharski 	timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
168*1b8adde7SWilliam Kucharski 	while(__timer_running());
169*1b8adde7SWilliam Kucharski }
170*1b8adde7SWilliam Kucharski void ndelay(unsigned int nsecs)
171*1b8adde7SWilliam Kucharski {
172*1b8adde7SWilliam Kucharski 	unsigned long long now;
173*1b8adde7SWilliam Kucharski 	rdtscll(now);
174*1b8adde7SWilliam Kucharski 	timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
175*1b8adde7SWilliam Kucharski 	while(__timer_running());
176*1b8adde7SWilliam Kucharski }
177*1b8adde7SWilliam Kucharski 
178*1b8adde7SWilliam Kucharski void load_timer2(unsigned int timer2_ticks)
179*1b8adde7SWilliam Kucharski {
180*1b8adde7SWilliam Kucharski 	unsigned long long now;
181*1b8adde7SWilliam Kucharski 	unsigned long clocks;
182*1b8adde7SWilliam Kucharski 	rdtscll(now);
183*1b8adde7SWilliam Kucharski 	clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
184*1b8adde7SWilliam Kucharski 	timer_timeout = now + clocks;
185*1b8adde7SWilliam Kucharski }
186*1b8adde7SWilliam Kucharski 
187*1b8adde7SWilliam Kucharski int timer2_running(void)
188*1b8adde7SWilliam Kucharski {
189*1b8adde7SWilliam Kucharski 	return __timer_running();
190*1b8adde7SWilliam Kucharski }
191*1b8adde7SWilliam Kucharski 
192*1b8adde7SWilliam Kucharski #endif /* RTC_CURRTICKS */
193