29,6 → 29,7 |
#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/sys/ll/event.h> |
#include <ll/sys/ll/time.h> |
|
36,6 → 37,7 |
|
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 |
|
//Max single delta_clk_per_msec increment = clk_per_msec / MAX_DIV_INK; |
#define MAX_DIV_INK 30000 |
49,6 → 51,8 |
signed long long clk_per_msec; |
signed long long * ptr_clk_per_msec = &clk_per_msec; |
|
signed long long apic_clk_per_msec; |
|
signed long last_delta_clk_per_msec; |
signed long total_delta_clk_per_msec; |
|
247,7 → 251,7 |
signed long long irq8_start; |
signed long long irq8_end; |
|
void calibrate_IRQ8(void *p) |
void calibrate_tsc_IRQ8(void *p) |
{ |
|
unsigned char set; |
285,7 → 289,7 |
|
cli(); |
|
irq_bind(8, calibrate_IRQ8, INT_FORCE); |
irq_bind(8, calibrate_tsc_IRQ8, INT_FORCE); |
|
CMOS_READ(0x0A,save_CMOS_regA); |
CMOS_READ(0x0B,save_CMOS_regB); |
305,7 → 309,7 |
|
clk_per_msec = dtsc / 500; |
|
message("Calibrated Clk_per_msec = %10ld\n",(long)clk_per_msec); |
message("Calibrated CPU Clk/msec = %10ld\n",(long)clk_per_msec); |
|
cli(); |
|
365,6 → 369,285 |
|
} |
|
int apic_get_maxlvt(void) |
{ |
unsigned int v, ver, maxlvt; |
|
v = apic_read(APIC_LVR); |
ver = GET_APIC_VERSION(v); |
/* 82489DXs do not report # of LVT entries. */ |
maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2; |
return maxlvt; |
} |
|
void clear_local_APIC(void) |
{ |
int maxlvt; |
unsigned long v; |
|
maxlvt = apic_get_maxlvt(); |
|
/* |
* Masking an LVT entry on a P6 can trigger a local APIC error |
* if the vector is zero. Mask LVTERR first to prevent this. |
*/ |
if (maxlvt >= 3) { |
v = 0xFF; /* any non-zero vector will do */ |
apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED); |
} |
/* |
* Careful: we have to set masks only first to deassert |
* any level-triggered sources. |
*/ |
v = apic_read(APIC_LVTT); |
apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); |
v = apic_read(APIC_LVT0); |
apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); |
v = apic_read(APIC_LVT1); |
apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED); |
if (maxlvt >= 4) { |
v = apic_read(APIC_LVTPC); |
apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED); |
} |
|
/* |
* Clean APIC state for other OSs: |
*/ |
apic_write_around(APIC_LVTT, APIC_LVT_MASKED); |
apic_write_around(APIC_LVT0, APIC_LVT_MASKED); |
apic_write_around(APIC_LVT1, APIC_LVT_MASKED); |
if (maxlvt >= 3) |
apic_write_around(APIC_LVTERR, APIC_LVT_MASKED); |
if (maxlvt >= 4) |
apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); |
v = GET_APIC_VERSION(apic_read(APIC_LVR)); |
if (APIC_INTEGRATED(v)) { /* !82489DX */ |
if (maxlvt > 3) |
apic_write(APIC_ESR, 0); |
apic_read(APIC_ESR); |
} |
} |
|
void connect_bsp_APIC(void) |
{ |
/* |
* Do not trust the local APIC being empty at bootup. |
*/ |
clear_local_APIC(); |
/* |
* PIC mode, enable APIC mode in the IMCR, i.e. |
* connect BSP's local APIC to INT and NMI lines. |
*/ |
outp(0x22, 0x70); |
outp(0x23, 0x01); |
|
} |
|
void disconnect_bsp_APIC(void) |
{ |
|
/* |
* Put the board back into PIC mode (has an effect |
* only on certain older boards). Note that APIC |
* interrupts, including IPIs, won't work beyond |
* this point! The only exception are INIT IPIs. |
*/ |
|
outp(0x22, 0x70); |
outp(0x23, 0x00); |
|
} |
|
void disable_local_APIC(void) |
{ |
unsigned long value; |
|
clear_local_APIC(); |
|
/* |
* Disable APIC (implies clearing of registers |
* for 82489DX!). |
*/ |
value = apic_read(APIC_SPIV); |
value &= ~APIC_SPIV_APIC_ENABLED; |
apic_write_around(APIC_SPIV, value); |
} |
|
#define SPURIOUS_APIC_VECTOR 0xFF |
|
/* |
* An initial setup of the virtual wire mode. |
*/ |
|
void setup_local_APIC (void) |
{ |
unsigned long value, ver; |
|
/* Pound the ESR really hard over the head with a big hammer - mbligh */ |
|
apic_write(APIC_ESR, 0); |
apic_write(APIC_ESR, 0); |
apic_write(APIC_ESR, 0); |
apic_write(APIC_ESR, 0); |
|
value = apic_read(APIC_LVR); |
ver = GET_APIC_VERSION(value); |
|
/* |
* Set Task Priority to 'accept all'. We never change this |
* later on. |
*/ |
value = apic_read(APIC_TASKPRI); |
value &= ~APIC_TPRI_MASK; |
apic_write_around(APIC_TASKPRI, value); |
|
/* |
* Now that we are all set up, enable the APIC |
*/ |
value = apic_read(APIC_SPIV); |
value &= ~APIC_VECTOR_MASK; |
/* |
* Enable APIC |
*/ |
value |= APIC_SPIV_APIC_ENABLED; |
|
/* |
* Some unknown Intel IO/APIC (or APIC) errata is biting us with |
* certain networking cards. If high frequency interrupts are |
* happening on a particular IOAPIC pin, plus the IOAPIC routing |
* entry is masked/unmasked at a high rate as well then sooner or |
* later IOAPIC line gets 'stuck', no more interrupts are received |
* from the device. If focus CPU is disabled then the hang goes |
* away, oh well :-( |
* |
* [ This bug can be reproduced easily with a level-triggered |
* PCI Ne2000 networking cards and PII/PIII processors, dual |
* BX chipset. ] |
*/ |
/* |
* Actually disabling the focus CPU check just makes the hang less |
* frequent as it makes the interrupt distributon model be more |
* like LRU than MRU (the short-term load is more even across CPUs). |
* See also the comment in end_level_ioapic_irq(). --macro |
*/ |
#if 1 |
/* Enable focus processor (bit==0) */ |
value &= ~APIC_SPIV_FOCUS_DISABLED; |
#else |
/* Disable focus processor (bit==1) */ |
value |= APIC_SPIV_FOCUS_DISABLED; |
#endif |
/* |
* Set spurious IRQ vector |
*/ |
value |= SPURIOUS_APIC_VECTOR; |
apic_write_around(APIC_SPIV, value); |
|
/* |
* Set up LVT0, LVT1: |
* |
* set up through-local-APIC on the BP's LINT0. This is not |
* strictly necessery in pure symmetric-IO mode, but sometimes |
* we delegate interrupts to the 8259A. |
*/ |
/* |
* TODO: set up through-local-APIC from through-I/O-APIC? --macro |
*/ |
value = APIC_DM_EXTINT; |
apic_write_around(APIC_LVT0, value); |
|
value = APIC_DM_NMI | APIC_LVT_MASKED; |
if (!APIC_INTEGRATED(ver)) /* 82489DX */ |
value |= APIC_LVT_LEVEL_TRIGGER; |
apic_write_around(APIC_LVT1, value); |
|
} |
|
void disable_APIC_timer(void) |
{ |
unsigned long v; |
|
v = apic_read(APIC_LVTT); |
apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); |
|
} |
|
void enable_APIC_timer(void) |
{ |
unsigned long v; |
|
v = apic_read(APIC_LVTT); |
apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); |
|
} |
|
#define LOCAL_TIMER_VECTOR 0x40 |
|
void setup_APIC_LVTT(unsigned int clocks) |
{ |
unsigned int lvtt1_value, tmp_value; |
|
lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | |
APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; |
apic_write_around(APIC_LVTT, lvtt1_value); |
|
/* |
* Divide PICLK by 1 |
*/ |
tmp_value = apic_read(APIC_TDCR); |
apic_write_around(APIC_TDCR, (tmp_value |
& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
| APIC_TDR_DIV_1); |
|
apic_write_around(APIC_TMICT, clocks); |
} |
|
#define APIC_LIMIT 0xFF000000 |
|
void ll_calibrate_apic(void) |
{ |
|
unsigned int apic_start = 0, apic_end = 0, dapic; |
signed long long tsc_start = 0, tsc_end = 0, dtsc; |
unsigned int tmp_value; |
|
cli(); |
|
tmp_value = apic_read(APIC_TDCR); |
apic_write_around(APIC_TDCR, (tmp_value |
& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
| APIC_TDR_DIV_1); |
|
apic_write(APIC_TMICT, MAX_DWORD); |
|
barrier(); |
rdtscll(tsc_start); |
barrier(); |
apic_start = apic_read(APIC_TMCCT); |
barrier(); |
|
while (apic_read(APIC_TMCCT) > APIC_LIMIT) { |
barrier(); |
rdtscll(tsc_end); |
} |
|
barrier(); |
rdtscll(tsc_end); |
barrier(); |
apic_end = apic_read(APIC_TMCCT); |
barrier(); |
|
sti(); |
|
dtsc = tsc_end - tsc_start; |
dapic = apic_start - apic_end; |
|
apic_clk_per_msec = clk_per_msec * (signed long long)(dapic) / dtsc; |
|
message("Calibrated APIC Clk/msec = %10ld\n",(long)apic_clk_per_msec); |
|
} |
|
void ll_init_advtimer() |
{ |
|
382,6 → 665,24 |
rdtscll(init_tsc); // Read start TSC |
init_nsec = 0; |
|
if (use_apic) { |
unsigned long msr_low_orig, tmp; |
|
cli(); |
|
rdmsr(APIC_BASE_MSR, msr_low_orig, tmp); |
wrmsr(APIC_BASE_MSR, msr_low_orig|(1<<11), 0); |
|
connect_bsp_APIC(); |
|
setup_local_APIC(); |
|
sti(); |
|
ll_calibrate_apic(); |
|
} |
|
if (use_cmos) { |
|
message("CMOS adjustement enabled\n"); |