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 10#include "etherboot.h" 11#include "timer.h" 12 13void load_timer2(unsigned int ticks) 14{ 15 /* Set up the timer gate, turn off the speaker */ 16 outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); 17 outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); 18 outb(ticks & 0xFF, TIMER2_PORT); 19 outb(ticks >> 8, TIMER2_PORT); 20} 21 22#if defined(CONFIG_TSC_CURRTICKS) 23#define rdtsc(low,high) \ 24 __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) 25 26#define rdtscll(val) \ 27 __asm__ __volatile__ ("rdtsc" : "=A" (val)) 28 29 30#define HZ TICKS_PER_SEC 31#define CLOCK_TICK_RATE 1193180U /* Underlying HZ */ 32/* LATCH is used in the interval timer and ftape setup. */ 33#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ 34 35 36/* ------ Calibrate the TSC ------- 37 * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). 38 * Too much 64-bit arithmetic here to do this cleanly in C, and for 39 * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) 40 * output busy loop as low as possible. We avoid reading the CTC registers 41 * directly because of the awkward 8-bit access mechanism of the 82C54 42 * device. 43 */ 44 45#define CALIBRATE_LATCH (5 * LATCH) 46 47static unsigned long long calibrate_tsc(void) 48{ 49 /* Set the Gate high, disable speaker */ 50 outb((inb(0x61) & ~0x02) | 0x01, 0x61); 51 52 /* 53 * Now let's take care of CTC channel 2 54 * 55 * Set the Gate high, program CTC channel 2 for mode 0, 56 * (interrupt on terminal count mode), binary count, 57 * load 5 * LATCH count, (LSB and MSB) to begin countdown. 58 */ 59 outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ 60 outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ 61 outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ 62 63 { 64 unsigned long startlow, starthigh; 65 unsigned long endlow, endhigh; 66 unsigned long count; 67 68 rdtsc(startlow,starthigh); 69 count = 0; 70 do { 71 count++; 72 } while ((inb(0x61) & 0x20) == 0); 73 rdtsc(endlow,endhigh); 74 75 /* Error: ECTCNEVERSET */ 76 if (count <= 1) 77 goto bad_ctc; 78 79 /* 64-bit subtract - gcc just messes up with long longs */ 80 __asm__("subl %2,%0\n\t" 81 "sbbl %3,%1" 82 :"=a" (endlow), "=d" (endhigh) 83 :"g" (startlow), "g" (starthigh), 84 "0" (endlow), "1" (endhigh)); 85 86 /* Error: ECPUTOOFAST */ 87 if (endhigh) 88 goto bad_ctc; 89 90 endlow /= 5; 91 return endlow; 92 } 93 94 /* 95 * The CTC wasn't reliable: we got a hit on the very first read, 96 * or the CPU was so fast/slow that the quotient wouldn't fit in 97 * 32 bits.. 98 */ 99bad_ctc: 100 printf("bad_ctc\n"); 101 return 0; 102} 103 104 105unsigned long currticks(void) 106{ 107 static unsigned long clocks_per_tick; 108 unsigned long clocks_high, clocks_low; 109 unsigned long currticks; 110 if (!clocks_per_tick) { 111 clocks_per_tick = calibrate_tsc(); 112 printf("clocks_per_tick = %d\n", clocks_per_tick); 113 } 114 115 /* Read the Time Stamp Counter */ 116 rdtsc(clocks_low, clocks_high); 117 118 /* currticks = clocks / clocks_per_tick; */ 119 __asm__("divl %1" 120 :"=a" (currticks) 121 :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high)); 122 123 124 return currticks; 125} 126 127#endif /* RTC_CURRTICKS */ 128