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