25,145 → 25,30 |
|
#include <ll/i386/stdlib.h> |
#include <ll/i386/error.h> |
#include <ll/i386/advtimer.h> |
#include <ll/sys/ll/ll-data.h> |
#include <ll/sys/ll/ll-func.h> |
#include <ll/i386/pic.h> |
#include <ll/i386/apic.h> |
#include <ll/i386/64bit.h> |
#include <ll/sys/ll/event.h> |
#include <ll/sys/ll/time.h> |
#include <ll/i386/advtimer.h> |
|
#define CALIBRATE_USING_CMOS |
|
unsigned char use_tsc = 1; //Enable the TSC counter mode |
unsigned char use_cmos = 0; //Enable the RTC correction |
unsigned char use_apic = 0; //Enable the APIC for P6 only |
unsigned long long init_tsc; |
unsigned long long * ptr_init_tsc = &init_tsc; |
|
//Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK; |
#define MAX_DIV_INK 30000 |
unsigned long long init_nsec; //Wraparound 292 years |
unsigned long long * ptr_init_nsec = &init_nsec; |
|
signed long long init_tsc; |
signed long long * ptr_init_tsc = &init_tsc; |
unsigned int clk_per_msec = 0; |
unsigned int apic_clk_per_msec = 0; |
unsigned int apic_set_limit = 0; |
|
signed long long init_nsec; //Wraparound 292 years |
signed long long * ptr_init_nsec = &init_nsec; |
|
signed long long clk_per_msec; |
signed long long * ptr_clk_per_msec = &clk_per_msec; |
|
signed long long apic_clk_per_msec; |
unsigned int apic_set_limit; |
|
signed long last_delta_clk_per_msec; |
signed long total_delta_clk_per_msec; |
|
unsigned char save_CMOS_regA; |
unsigned char save_CMOS_regB; |
|
//#define IRQ8_DEBUG |
|
void HandlerIRQ8(void *p) |
{ |
|
unsigned char set; |
|
static unsigned long Mconst = 1000000; |
|
static unsigned long init_step = 0; |
|
signed long max_dcms = clk_per_msec / MAX_DIV_INK; |
|
static signed long long dn; |
static signed long long * ptr_dn = &dn; |
signed long delta_clk_per_msec; |
|
cli(); |
|
#ifdef IRQ8_DEBUG |
message("(IRQ8"); |
#endif |
|
CMOS_READ(0x0C,set); |
|
__asm__("xorl %%eax,%%eax\n\t" |
"cpuid\n\t" |
"rdtsc\n\t" |
"pushl %%eax\n\t" |
"pushl %%edx\n\t" |
"pushl %%eax\n\t" |
"pushl %%edx\n\t" |
"xorl %%eax,%%eax\n\t" |
"cpuid\n\t" |
"popl %%edx\n\t" |
"popl %%eax\n\t" |
"subl (%%edi),%%eax\n\t" |
"sbbl 4(%%edi),%%edx\n\t" |
"popl 4(%%edi)\n\t" |
"popl (%%edi)\n\t" |
"movl %%edx,%%ecx\n\t" |
"mull %4\n\t" |
"pushl %%eax\n\t" |
"movl %%ecx,%%eax\n\t" |
"movl %%edx,%%ecx\n\t" |
"mull %4\n\t" |
"addl %%ecx,%%eax\n\t" |
"adcl $0,%%edx\n\t" |
"movl %7,%%ebx\n\t" |
"divl (%%ebx)\n\t" |
"movl %%eax,4(%%esi)\n\t" |
"popl %%eax\n\t" |
"divl (%%ebx)\n\t" |
"movl %%eax,(%%esi)\n\t" |
|
: |
: "D" (ptr_init_tsc), "S" (ptr_dn), "b" (0), |
"c" (0), "m" (Mconst), "a" (0), "d" (0), "m" (ptr_clk_per_msec)); |
|
//Offset |
init_nsec += dn; |
|
if (init_step < 5) { |
init_step++; |
#ifdef IRQ8_DEBUG |
message(")"); |
#endif |
|
sti(); |
|
return; |
} |
|
dn = dn % 1000000000 - 500000000; |
|
//Delta clk/msec |
delta_clk_per_msec = dn * clk_per_msec / (500000000 - dn); |
|
//clk_per_msec adjustment |
if (delta_clk_per_msec < 0) { |
|
if (delta_clk_per_msec > -max_dcms) |
clk_per_msec += delta_clk_per_msec; |
else |
clk_per_msec -= max_dcms; |
} else { |
|
if (delta_clk_per_msec < max_dcms) |
clk_per_msec += delta_clk_per_msec; |
else |
clk_per_msec += max_dcms; |
} |
|
last_delta_clk_per_msec = delta_clk_per_msec; |
total_delta_clk_per_msec += delta_clk_per_msec; |
|
#ifdef IRQ8_DEBUG |
message(")"); |
#endif |
|
sti(); |
|
} |
|
#ifdef CONFIG_MELAN |
# define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ |
#else |
178,11 → 63,11 |
void ll_calibrate_tsc(void) |
{ |
|
signed long long start; |
signed long long end; |
signed long long dtsc; |
unsigned long long start; |
unsigned long long end; |
unsigned long long dtsc; |
|
signed long start_8253, end_8253, delta_8253; |
unsigned int start_8253, end_8253, delta_8253; |
|
cli(); |
|
231,13 → 116,13 |
ll_abort(10); |
} |
|
message("Delta TSC = %10ld\n",(long)dtsc); |
message("Delta TSC = %10d\n",(int)dtsc); |
|
message("Delta PIT = %10ld\n",(long)delta_8253); |
message("Delta PIT = %10d\n",delta_8253); |
|
clk_per_msec = dtsc * CLOCK_TICK_RATE / delta_8253 / 1000; |
|
message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec); |
message("Calibrated Clk_per_msec = %10d\n",clk_per_msec); |
|
sti(); |
|
249,8 → 134,8 |
#define CMOS_END 3 |
|
int cmos_calibrate_status = CMOS_INIT; |
signed long long irq8_start; |
signed long long irq8_end; |
unsigned long long irq8_start; |
unsigned long long irq8_end; |
|
void calibrate_tsc_IRQ8(void *p) |
{ |
286,7 → 171,7 |
void ll_calibrate_tsc_cmos(void) |
{ |
|
signed long long dtsc; |
unsigned long long dtsc; |
|
cli(); |
|
310,7 → 195,7 |
|
clk_per_msec = dtsc / 500; |
|
message("Calibrated CPU Clk/msec = %10ld\n",(long)clk_per_msec); |
message("Calibrated CPU Clk/msec = %10d\n",clk_per_msec); |
|
cli(); |
|
323,53 → 208,6 |
|
} |
|
//Low level time read function |
void ll_read_timespec(struct timespec *tspec) |
{ |
|
static unsigned long Gconst = 1000000000; |
static unsigned long Mconst = 1000000; |
|
if (clk_per_msec <= 0) { |
NULL_TIMESPEC(tspec); |
return; |
} |
|
__asm__("xorl %%eax,%%eax\n\t" |
"cpuid\n\t" |
"rdtsc\n\t" |
"pushl %%eax\n\t" |
"pushl %%edx\n\t" |
"xorl %%eax,%%eax\n\t" |
"cpuid\n\t" |
"popl %%edx\n\t" |
"popl %%eax\n\t" |
"subl (%%edi),%%eax\n\t" |
"sbbl 4(%%edi),%%edx\n\t" |
"movl %%edx,%%ecx\n\t" |
"mull %6\n\t" |
"pushl %%eax\n\t" |
"movl %%ecx,%%eax\n\t" |
"movl %%edx,%%ecx\n\t" |
"mull %6\n\t" |
"addl %%ecx,%%eax\n\t" |
"adcl $0,%%edx\n\t" |
"movl %8,%%ebx\n\t" |
"divl (%%ebx)\n\t" |
"movl %%eax,%%ecx\n\t" |
"popl %%eax\n\t" |
"divl (%%ebx)\n\t" |
"movl %%ecx,%%edx\n\t" |
"addl (%%esi),%%eax\n\t" |
"adcl 4(%%esi),%%edx\n\t" |
"divl %7\n\t" |
|
: "=a" (tspec->tv_sec), "=d" (tspec->tv_nsec) |
: "D" (ptr_init_tsc), "S" (ptr_init_nsec), "b" (0), |
"c" (0), "m" (Mconst), "m" (Gconst), "m" (ptr_clk_per_msec)); |
|
} |
|
int apic_get_maxlvt(void) |
{ |
unsigned int v, ver, maxlvt; |
523,7 → 361,7 |
{ |
|
unsigned int apic_start = 0, apic_end = 0, dapic; |
signed long long tsc_start = 0, tsc_end = 0, dtsc; |
unsigned long long tsc_start = 0, tsc_end = 0, dtsc; |
unsigned int tmp_value; |
|
cli(); |
557,17 → 395,20 |
dtsc = tsc_end - tsc_start; |
dapic = apic_start - apic_end; |
|
apic_clk_per_msec = clk_per_msec * (signed long long)(dapic) / dtsc; |
apic_clk_per_msec = (unsigned long long)(clk_per_msec) * (unsigned long long)(dapic) / dtsc; |
apic_set_limit = ((apic_clk_per_msec / 100) == 0) ? (apic_clk_per_msec/100) : APIC_SET_LIMIT; |
|
message("Calibrated APIC Clk/msec = %10ld\n",(long)apic_clk_per_msec); |
message("Calibrated APIC Clk/msec = %10d\n",apic_clk_per_msec); |
|
} |
|
void ll_init_advtimer() |
{ |
#ifdef __APIC__ |
unsigned long msr_low_orig, tmp; |
#endif |
|
if (use_tsc) { |
#ifdef __TSC__ |
|
#ifdef CALIBRATE_USING_CMOS |
ll_calibrate_tsc_cmos(); |
575,14 → 416,10 |
ll_calibrate_tsc(); |
#endif |
|
last_delta_clk_per_msec = 0; |
total_delta_clk_per_msec = 0; |
|
rdtscll(init_tsc); // Read start TSC |
init_nsec = 0; |
|
if (use_apic) { |
unsigned long msr_low_orig, tmp; |
#ifdef __APIC__ |
|
rdmsr(APIC_BASE_MSR, msr_low_orig, tmp); |
wrmsr(APIC_BASE_MSR, msr_low_orig|(1<<11), 0); |
595,53 → 432,17 |
|
setup_APIC_timer(); |
|
} |
#endif |
|
if (use_cmos) { |
#endif |
|
message("CMOS adjustement enabled\n"); |
|
cli(); |
|
irq_bind(8, HandlerIRQ8, INT_FORCE); |
|
CMOS_READ(0x0A,save_CMOS_regA); |
CMOS_READ(0x0B,save_CMOS_regB); |
|
CMOS_WRITE(0x0A,0x2F); // Set 2 Hz Periodic Interrupt |
CMOS_WRITE(0x0B,0x42); // Enable Interrupt |
|
irq_unmask(8); |
|
sti(); |
|
} |
|
} else { |
|
use_cmos = 0; |
|
} |
|
} |
|
void ll_restore_adv() |
{ |
/* Restore CMOS setting */ |
if (use_cmos) { |
cli(); |
|
irq_mask(8); |
|
CMOS_WRITE(0x0A,save_CMOS_regA); |
CMOS_WRITE(0x0B,save_CMOS_regB); |
|
sti(); |
} |
|
/* Disable APIC */ |
if (use_apic) { |
unsigned int msr_low_orig, tmp; |
#ifdef __APIC__ |
unsigned int msr_low_orig, tmp; |
|
cli(); |
|
652,6 → 453,6 |
|
sti(); |
|
} |
#endif |
|
} |