2 * This file is part of the coreboot project.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include <commonlib/helpers.h>
16 #include <cpu/x86/tsc.h>
17 #include <pc80/i8254.h>
19 /* Initialize i8254 timers */
21 void setup_i8254(void)
23 /* Timer 0 (taken from biosemu) */
24 outb(TIMER0_SEL
| WORD_ACCESS
| MODE3
| BINARY_COUNT
, TIMER_MODE_PORT
);
25 outb(0x00, TIMER0_PORT
);
26 outb(0x00, TIMER0_PORT
);
29 outb(TIMER1_SEL
| LOBYTE_ACCESS
| MODE3
| BINARY_COUNT
,
31 outb(0x12, TIMER1_PORT
);
34 #define CLOCK_TICK_RATE 1193180U /* Underlying HZ */
36 /* ------ Calibrate the TSC -------
37 * Too much 64-bit arithmetic here to do this cleanly in C, and for
38 * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
39 * output busy loop as low as possible. We avoid reading the CTC registers
40 * directly because of the awkward 8-bit access mechanism of the 82C54
44 #define CALIBRATE_INTERVAL ((2*CLOCK_TICK_RATE)/1000) /* 2ms */
45 #define CALIBRATE_DIVISOR (2*1000) /* 2ms / 2000 == 1usec */
47 unsigned long calibrate_tsc_with_pit(void)
49 /* Set the Gate high, disable speaker */
50 outb((inb(0x61) & ~0x02) | 0x01, 0x61);
53 * Now let's take care of CTC channel 2
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.
59 outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
61 outb(CALIBRATE_INTERVAL
& 0xff, 0x42); /* LSB of count */
62 outb(CALIBRATE_INTERVAL
>> 8, 0x42); /* MSB of count */
73 } while ((inb(0x61) & 0x20) == 0);
76 /* Error: ECTCNEVERSET */
80 /* 64-bit subtract - gcc just messes up with long longs */
81 __asm__("subl %2,%0\n\t"
83 : "=a" (end
.lo
), "=d" (end
.hi
)
84 : "g" (start
.lo
), "g" (start
.hi
),
85 "0" (end
.lo
), "1" (end
.hi
));
87 /* Error: ECPUTOOFAST */
92 /* Error: ECPUTOOSLOW */
93 if (end
.lo
<= CALIBRATE_DIVISOR
)
96 return DIV_ROUND_UP(end
.lo
, CALIBRATE_DIVISOR
);
100 * The CTC wasn't reliable: we got a hit on the very first read,
101 * or the CPU was so fast/slow that the quotient wouldn't fit in
108 #if CONFIG(UNKNOWN_TSC_RATE)
109 static u32 timer_tsc
;
111 unsigned long tsc_freq_mhz(void)
116 timer_tsc
= calibrate_tsc_with_pit();
118 /* Set some semi-ridiculous rate if approximation fails. */