From a9e511dfa65ff6278e563186856c43d5eb12f438 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Sat, 2 May 2009 21:35:59 +0800 Subject: [PATCH] lapic timer: Correct AMD C1E handling It turns out that AMD C1E only happens after ACPI-CA module is running, so we will have to broadcast IPI at the end of the ACPI-CA attach to clear the C1E related bits and kick start the possible stalled lapic timer. Tested-with: TL-58 --- sys/dev/acpica5/acpi.c | 12 +++++ sys/platform/pc32/apic/mpapic.c | 81 +++++++++++++++++++++++---------- sys/platform/pc64/apic/mpapic.c | 7 +++ 3 files changed, 77 insertions(+), 23 deletions(-) diff --git a/sys/dev/acpica5/acpi.c b/sys/dev/acpica5/acpi.c index f86d43f84a..4e570bd982 100644 --- a/sys/dev/acpica5/acpi.c +++ b/sys/dev/acpica5/acpi.c @@ -65,6 +65,10 @@ MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices"); #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("ACPI") +#ifdef SMP +extern void lapic_timer_fixup(void); +#endif + static d_open_t acpiopen; static d_close_t acpiclose; static d_ioctl_t acpiioctl; @@ -694,6 +698,14 @@ acpi_attach(device_t dev) out: ACPI_UNLOCK; + +#ifdef SMP + /* + * See the comment near lapic_timer_fixup() in + * platform/pc32/apic/mpapic.c + */ + lapic_timer_fixup(); +#endif return_VALUE (error); } diff --git a/sys/platform/pc32/apic/mpapic.c b/sys/platform/pc32/apic/mpapic.c index 1fa6366a99..5a1c6f6821 100644 --- a/sys/platform/pc32/apic/mpapic.c +++ b/sys/platform/pc32/apic/mpapic.c @@ -45,7 +45,9 @@ static void lapic_timer_calibrate(void); static void lapic_timer_set_divisor(int); static void lapic_timer_intr_reload(sysclock_t); +static void lapic_timer_fixup_handler(void *); +void lapic_timer_fixup(void); void lapic_timer_process(void); void lapic_timer_process_frame(struct intrframe *); void lapic_timer_intr_test(void); @@ -169,29 +171,6 @@ apic_initialize(boolean_t bsp) lapic.eoi = 0; lapic.eoi = 0; - if (strcmp(cpu_vendor, "AuthenticAMD") == 0) { - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. This feature renders - * the local APIC timer dead, so we disable it by reading - * the Interrupt Pending Message register and clearing both - * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). - * - * Reference: - * "BIOS and Kernel Developer's Guide for AMD NPT - * Family 0Fh Processors" - * #32559 revision 3.00 - */ - if ((cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - uint64_t msr; - - msr = rdmsr(0xc0010055); - if (msr & 0x18000000) - wrmsr(0xc0010055, msr & ~0x18000000ULL); - } - } - if (bsp) { lapic_timer_calibrate(); if (lapic_timer_enable) @@ -329,6 +308,62 @@ lapic_timer_oneshot_intr_enable(void) timer = lapic.lvt_timer; timer &= ~(APIC_LVTT_MASKED | APIC_LVTT_PERIODIC); lapic.lvt_timer = timer; + + lapic_timer_fixup_handler(NULL); +} + +static void +lapic_timer_fixup_handler(void *dummy __unused) +{ + if (strcmp(cpu_vendor, "AuthenticAMD") == 0) { + /* + * Detect the presence of C1E capability mostly on latest + * dual-cores (or future) k8 family. This feature renders + * the local APIC timer dead, so we disable it by reading + * the Interrupt Pending Message register and clearing both + * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). + * + * Reference: + * "BIOS and Kernel Developer's Guide for AMD NPT + * Family 0Fh Processors" + * #32559 revision 3.00 + */ + if ((cpu_id & 0x00000f00) == 0x00000f00 && + (cpu_id & 0x0fff0000) >= 0x00040000) { + uint64_t msr; + + msr = rdmsr(0xc0010055); + if (msr & 0x18000000) { + struct globaldata *gd = mycpu; + + kprintf("cpu%d: AMD C1E detected\n", + gd->gd_cpuid); + wrmsr(0xc0010055, msr & ~0x18000000ULL); + + /* + * We are kinda stalled; + * kick start again. + */ + gd->gd_timer_running = 1; + lapic_timer_oneshot_quick(2); + } + } + } +} + +/* + * This function is called only by ACPI-CA code currently: + * - AMD C1E fixup. AMD C1E only seems to happen after ACPI + * module controls PM. So once ACPI-CA is attached, we try + * to apply the fixup to prevent LAPIC timer from hanging. + */ +void +lapic_timer_fixup(void) +{ + if (lapic_timer_test || lapic_timer_enable) { + lwkt_send_ipiq_mask(smp_active_mask, + lapic_timer_fixup_handler, NULL); + } } diff --git a/sys/platform/pc64/apic/mpapic.c b/sys/platform/pc64/apic/mpapic.c index ccd929101c..3c68f280c7 100644 --- a/sys/platform/pc64/apic/mpapic.c +++ b/sys/platform/pc64/apic/mpapic.c @@ -47,6 +47,8 @@ volatile ioapic_t **ioapic; +void lapic_timer_fixup(void); + /* * Enable APIC, configure interrupts. */ @@ -140,6 +142,11 @@ apic_initialize(void) apic_dump("apic_initialize()"); } +void +lapic_timer_fixup(void) +{ + /* TODO */ +} /* * dump contents of local APIC registers -- 2.41.0