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