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
__load_timer2(unsigned int ticks)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
__timer2_running(void)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)
setup_timers(void)44*1b8adde7SWilliam Kucharski void setup_timers(void)
45*1b8adde7SWilliam Kucharski {
46*1b8adde7SWilliam Kucharski return;
47*1b8adde7SWilliam Kucharski }
48*1b8adde7SWilliam Kucharski
load_timer2(unsigned int ticks)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
timer2_running(void)54*1b8adde7SWilliam Kucharski int timer2_running(void)
55*1b8adde7SWilliam Kucharski {
56*1b8adde7SWilliam Kucharski return __timer2_running();
57*1b8adde7SWilliam Kucharski }
58*1b8adde7SWilliam Kucharski
ndelay(unsigned int nsecs)59*1b8adde7SWilliam Kucharski void ndelay(unsigned int nsecs)
60*1b8adde7SWilliam Kucharski {
61*1b8adde7SWilliam Kucharski waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000);
62*1b8adde7SWilliam Kucharski }
udelay(unsigned int usecs)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
sleep_latch(void)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
calibrate_tsc(void)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;
setup_timers(void)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
currticks(void)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;
__timer_running(void)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
udelay(unsigned int usecs)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 }
ndelay(unsigned int nsecs)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
load_timer2(unsigned int timer2_ticks)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
timer2_running(void)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