2004-10-14 21:29:29 +02:00
|
|
|
#include <console/console.h>
|
|
|
|
#include <arch/io.h>
|
|
|
|
#include <cpu/x86/msr.h>
|
|
|
|
#include <cpu/x86/tsc.h>
|
|
|
|
#include <smp/spinlock.h>
|
|
|
|
#include <delay.h>
|
2013-05-06 19:22:23 +02:00
|
|
|
#include <thread.h>
|
2004-10-14 21:29:29 +02:00
|
|
|
|
2013-05-01 22:27:09 +02:00
|
|
|
#if !defined(__PRE_RAM__)
|
|
|
|
|
2004-10-14 21:29:29 +02:00
|
|
|
static unsigned long clocks_per_usec;
|
|
|
|
|
2013-05-01 22:27:09 +02:00
|
|
|
#if CONFIG_TSC_CONSTANT_RATE
|
|
|
|
static unsigned long calibrate_tsc(void)
|
|
|
|
{
|
|
|
|
return tsc_freq_mhz();
|
|
|
|
}
|
|
|
|
#else /* CONFIG_TSC_CONSTANT_RATE */
|
2010-09-08 12:58:02 +02:00
|
|
|
#if !CONFIG_TSC_CALIBRATE_WITH_IO
|
2004-10-14 21:29:29 +02:00
|
|
|
#define CLOCK_TICK_RATE 1193180U /* Underlying HZ */
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
/* ------ Calibrate the TSC -------
|
2004-10-14 21:29:29 +02:00
|
|
|
* Too much 64-bit arithmetic here to do this cleanly in C, and for
|
|
|
|
* accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
|
|
|
|
* output busy loop as low as possible. We avoid reading the CTC registers
|
|
|
|
* directly because of the awkward 8-bit access mechanism of the 82C54
|
|
|
|
* device.
|
|
|
|
*/
|
|
|
|
|
2010-09-07 09:53:26 +02:00
|
|
|
#define CALIBRATE_INTERVAL ((2*CLOCK_TICK_RATE)/1000) /* 2ms */
|
|
|
|
#define CALIBRATE_DIVISOR (2*1000) /* 2ms / 2000 == 1usec */
|
2004-10-14 21:29:29 +02:00
|
|
|
|
|
|
|
static unsigned long long calibrate_tsc(void)
|
|
|
|
{
|
|
|
|
/* Set the Gate high, disable speaker */
|
|
|
|
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now let's take care of CTC channel 2
|
|
|
|
*
|
|
|
|
* Set the Gate high, program CTC channel 2 for mode 0,
|
|
|
|
* (interrupt on terminal count mode), binary count,
|
|
|
|
* load 5 * LATCH count, (LSB and MSB) to begin countdown.
|
|
|
|
*/
|
|
|
|
outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
|
|
|
|
outb(CALIBRATE_INTERVAL & 0xff, 0x42); /* LSB of count */
|
|
|
|
outb(CALIBRATE_INTERVAL >> 8, 0x42); /* MSB of count */
|
|
|
|
|
|
|
|
{
|
|
|
|
tsc_t start;
|
|
|
|
tsc_t end;
|
|
|
|
unsigned long count;
|
|
|
|
|
|
|
|
start = rdtsc();
|
|
|
|
count = 0;
|
|
|
|
do {
|
|
|
|
count++;
|
|
|
|
} while ((inb(0x61) & 0x20) == 0);
|
|
|
|
end = rdtsc();
|
|
|
|
|
|
|
|
/* Error: ECTCNEVERSET */
|
|
|
|
if (count <= 1)
|
|
|
|
goto bad_ctc;
|
|
|
|
|
|
|
|
/* 64-bit subtract - gcc just messes up with long longs */
|
|
|
|
__asm__("subl %2,%0\n\t"
|
|
|
|
"sbbl %3,%1"
|
|
|
|
:"=a" (end.lo), "=d" (end.hi)
|
|
|
|
:"g" (start.lo), "g" (start.hi),
|
|
|
|
"0" (end.lo), "1" (end.hi));
|
|
|
|
|
|
|
|
/* Error: ECPUTOOFAST */
|
|
|
|
if (end.hi)
|
|
|
|
goto bad_ctc;
|
|
|
|
|
|
|
|
|
|
|
|
/* Error: ECPUTOOSLOW */
|
|
|
|
if (end.lo <= CALIBRATE_DIVISOR)
|
|
|
|
goto bad_ctc;
|
|
|
|
|
2014-07-07 17:53:24 +02:00
|
|
|
return CEIL_DIV(end.lo, CALIBRATE_DIVISOR);
|
2004-10-14 21:29:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The CTC wasn't reliable: we got a hit on the very first read,
|
|
|
|
* or the CPU was so fast/slow that the quotient wouldn't fit in
|
|
|
|
* 32 bits..
|
|
|
|
*/
|
|
|
|
bad_ctc:
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_ERR, "bad_ctc\n");
|
2004-10-14 21:29:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-09-08 12:58:02 +02:00
|
|
|
#else /* CONFIG_TSC_CALIBRATE_WITH_IO */
|
2004-10-14 21:29:29 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* this is the "no timer2" version.
|
|
|
|
* to calibrate tsc, we get a TSC reading, then do 1,000,000 outbs to port 0x80
|
|
|
|
* then we read TSC again, and divide the difference by 1,000,000
|
2010-04-27 08:56:47 +02:00
|
|
|
* we have found on a wide range of machines that this gives us a a
|
2004-10-14 21:29:29 +02:00
|
|
|
* good microsecond value
|
|
|
|
* to +- 10%. On a dual AMD 1.6 Ghz box, it gives us .97 microseconds, and on a
|
|
|
|
* 267 Mhz. p5, it gives us 1.1 microseconds.
|
|
|
|
* also, since gcc now supports long long, we use that.
|
|
|
|
* also no unsigned long long / operator, so we play games.
|
2010-04-27 08:56:47 +02:00
|
|
|
* about the only thing you can do with long longs, it seems,
|
2004-10-14 21:29:29 +02:00
|
|
|
*is return them and assign them.
|
|
|
|
* (and do asm on them, yuck)
|
|
|
|
* so avoid all ops on long longs.
|
|
|
|
*/
|
|
|
|
static unsigned long long calibrate_tsc(void)
|
|
|
|
{
|
|
|
|
unsigned long long start, end, delta;
|
2007-10-24 00:17:45 +02:00
|
|
|
unsigned long result, count;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_SPEW, "Calibrating delay loop...\n");
|
2004-10-14 21:29:29 +02:00
|
|
|
start = rdtscll();
|
2009-07-21 23:19:06 +02:00
|
|
|
// no udivdi3 because we don't like libgcc. (only in x86emu)
|
2004-10-14 21:29:29 +02:00
|
|
|
// so we count to 1<< 20 and then right shift 20
|
|
|
|
for(count = 0; count < (1<<20); count ++)
|
2009-07-21 23:19:06 +02:00
|
|
|
inb(0x80);
|
2004-10-14 21:29:29 +02:00
|
|
|
end = rdtscll();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// make delta be (endhigh - starthigh) + (endlow - startlow)
|
|
|
|
// but >> 20
|
|
|
|
// do it this way to avoid gcc warnings.
|
|
|
|
start = tsc_start.hi;
|
|
|
|
start <<= 32;
|
|
|
|
start |= start.lo;
|
|
|
|
end = tsc_end.hi;
|
|
|
|
end <<= 32;
|
|
|
|
end |= tsc_end.lo;
|
|
|
|
#endif
|
|
|
|
delta = end - start;
|
|
|
|
// at this point we have a delta for 1,000,000 outbs. Now rescale for one microsecond.
|
|
|
|
delta >>= 20;
|
|
|
|
// save this for microsecond timing.
|
|
|
|
result = delta;
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_SPEW, "end %llx, start %llx\n", end, start);
|
|
|
|
printk(BIOS_SPEW, "32-bit delta %ld\n", (unsigned long) delta);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_SPEW, "%s 32-bit result is %ld\n",
|
2009-02-12 22:30:06 +01:00
|
|
|
__func__,
|
2005-09-14 18:33:10 +02:00
|
|
|
result);
|
2004-10-14 21:29:29 +02:00
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-10 00:12:40 +02:00
|
|
|
#endif /* CONFIG_TSC_CALIBRATE_WITH_IO */
|
2013-05-01 22:27:09 +02:00
|
|
|
#endif /* CONFIG_TSC_CONSTANT_RATE */
|
2004-10-14 21:29:29 +02:00
|
|
|
|
|
|
|
void init_timer(void)
|
|
|
|
{
|
2015-06-09 22:02:50 +02:00
|
|
|
if (!clocks_per_usec)
|
2004-10-14 21:29:29 +02:00
|
|
|
clocks_per_usec = calibrate_tsc();
|
|
|
|
}
|
|
|
|
|
2013-05-01 22:27:09 +02:00
|
|
|
static inline unsigned long get_clocks_per_usec(void)
|
|
|
|
{
|
|
|
|
init_timer();
|
|
|
|
return clocks_per_usec;
|
|
|
|
}
|
|
|
|
#else /* !defined(__PRE_RAM__) */
|
|
|
|
/* romstage calls into cpu/board specific function every time. */
|
|
|
|
static inline unsigned long get_clocks_per_usec(void)
|
|
|
|
{
|
|
|
|
return tsc_freq_mhz();
|
|
|
|
}
|
|
|
|
#endif /* !defined(__PRE_RAM__) */
|
|
|
|
|
2004-10-14 21:29:29 +02:00
|
|
|
void udelay(unsigned us)
|
|
|
|
{
|
2013-05-01 22:55:14 +02:00
|
|
|
unsigned long long start;
|
|
|
|
unsigned long long current;
|
|
|
|
unsigned long long clocks;
|
2004-10-14 21:29:29 +02:00
|
|
|
|
2013-05-06 19:22:23 +02:00
|
|
|
if (!thread_yield_microseconds(us))
|
|
|
|
return;
|
|
|
|
|
2013-05-01 22:55:14 +02:00
|
|
|
start = rdtscll();
|
2004-10-14 21:29:29 +02:00
|
|
|
clocks = us;
|
2013-05-01 22:27:09 +02:00
|
|
|
clocks *= get_clocks_per_usec();
|
2013-05-01 22:55:14 +02:00
|
|
|
current = rdtscll();
|
|
|
|
while((current - start) < clocks) {
|
2004-10-14 21:29:29 +02:00
|
|
|
cpu_relax();
|
2013-05-01 22:55:14 +02:00
|
|
|
current = rdtscll();
|
|
|
|
}
|
2004-10-14 21:29:29 +02:00
|
|
|
}
|
2013-04-30 05:22:55 +02:00
|
|
|
|
2015-05-12 23:48:31 +02:00
|
|
|
#if CONFIG_TSC_MONOTONIC_TIMER && !defined(__PRE_RAM__)
|
2013-04-30 05:22:55 +02:00
|
|
|
#include <timer.h>
|
|
|
|
|
|
|
|
static struct monotonic_counter {
|
|
|
|
int initialized;
|
|
|
|
struct mono_time time;
|
|
|
|
uint64_t last_value;
|
|
|
|
} mono_counter;
|
|
|
|
|
|
|
|
void timer_monotonic_get(struct mono_time *mt)
|
|
|
|
{
|
|
|
|
uint64_t current_tick;
|
|
|
|
uint64_t ticks_elapsed;
|
|
|
|
|
|
|
|
if (!mono_counter.initialized) {
|
|
|
|
init_timer();
|
|
|
|
mono_counter.last_value = rdtscll();
|
|
|
|
mono_counter.initialized = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_tick = rdtscll();
|
|
|
|
ticks_elapsed = current_tick - mono_counter.last_value;
|
|
|
|
|
|
|
|
/* Update current time and tick values only if a full tick occurred. */
|
|
|
|
if (ticks_elapsed >= clocks_per_usec) {
|
|
|
|
uint64_t usecs_elapsed;
|
|
|
|
|
|
|
|
usecs_elapsed = ticks_elapsed / clocks_per_usec;
|
|
|
|
mono_time_add_usecs(&mono_counter.time, (long)usecs_elapsed);
|
|
|
|
mono_counter.last_value = current_tick;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save result. */
|
|
|
|
*mt = mono_counter.time;
|
|
|
|
}
|
|
|
|
#endif
|