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