x86/hpet: disable before reboot or kexec
authorJan Beulich <jbeulich@suse.com>
Tue, 27 Mar 2012 13:20:23 +0000 (15:20 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 27 Mar 2012 13:20:23 +0000 (15:20 +0200)
Linux up to now is not smart enough to properly clear the HPET when it
boots, which is particularly a problem when a kdump attempt from
running under Xen is being made. Linux itself added code to work around
this to its shutdown paths quite some time ago, so let's do something
similar in Xen: Save the configuration register settings during boot,
and restore them during shutdown. This should cover the majority of
cases where the secondary kernel might not come up because timer
interrupts don't work.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
xen/arch/x86/crash.c
xen/arch/x86/hpet.c
xen/arch/x86/smp.c
xen/arch/x86/time.c
xen/include/asm-x86/hpet.h

index 68174b51faca9b829c5ede8a5f1b73eabcc307e0..0e1afa67dd604779ecfce15d59ce494fc42fb71e 100644 (file)
@@ -94,6 +94,7 @@ static void nmi_shootdown_cpus(void)
     x2apic_enabled = (current_local_apic_mode() == APIC_MODE_X2APIC);
 
     disable_IO_APIC();
+    hpet_disable();
 }
 
 void machine_crash_shutdown(void)
index 8ead34be607be951e949e6a4cb24fbb76ceacfba..b5ef54f90a291870fdc057db15e972a6712fee5e 100644 (file)
@@ -562,7 +562,7 @@ void hpet_broadcast_resume(void)
     if ( !hpet_events )
         return;
 
-    hpet_resume();
+    hpet_resume(NULL);
 
     cfg = hpet_read32(HPET_CFG);
 
@@ -700,10 +700,13 @@ int hpet_legacy_irq_tick(void)
     return 1;
 }
 
+static u32 *hpet_boot_cfg;
+
 u64 __init hpet_setup(void)
 {
     static u64 __initdata hpet_rate;
     u32 hpet_id, hpet_period;
+    unsigned int last;
 
     if ( hpet_rate )
         return hpet_rate;
@@ -728,7 +731,9 @@ u64 __init hpet_setup(void)
         return 0;
     }
 
-    hpet_resume();
+    last = (hpet_id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
+    hpet_boot_cfg = xmalloc_array(u32, 2 + last);
+    hpet_resume(hpet_boot_cfg);
 
     hpet_rate = 1000000000000000ULL; /* 10^15 */
     (void)do_div(hpet_rate, hpet_period);
@@ -736,24 +741,29 @@ u64 __init hpet_setup(void)
     return hpet_rate;
 }
 
-void hpet_resume(void)
+void hpet_resume(u32 *boot_cfg)
 {
     static u32 system_reset_latch;
     u32 hpet_id, cfg;
-    unsigned int i;
+    unsigned int i, last;
 
     if ( system_reset_latch == system_reset_counter )
         return;
     system_reset_latch = system_reset_counter;
 
     cfg = hpet_read32(HPET_CFG);
+    if ( boot_cfg )
+        *boot_cfg = cfg;
     cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
     hpet_write32(cfg, HPET_CFG);
 
     hpet_id = hpet_read32(HPET_ID);
-    for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ )
+    last = (hpet_id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
+    for ( i = 0; i <= last; ++i )
     {
         cfg = hpet_read32(HPET_Tn_CFG(i));
+        if ( boot_cfg )
+            boot_cfg[i + 1] = cfg;
         cfg &= ~HPET_TN_ENABLE;
         hpet_write32(cfg, HPET_Tn_CFG(i));
     }
@@ -762,3 +772,21 @@ void hpet_resume(void)
     cfg |= HPET_CFG_ENABLE;
     hpet_write32(cfg, HPET_CFG);
 }
+
+void hpet_disable(void)
+{
+    unsigned int i;
+    u32 id;
+
+    if ( !hpet_boot_cfg )
+        return;
+
+    hpet_write32(*hpet_boot_cfg & ~HPET_CFG_ENABLE, HPET_CFG);
+
+    id = hpet_read32(HPET_ID);
+    for ( i = 0; i <= ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); ++i )
+        hpet_write32(hpet_boot_cfg[i + 1], HPET_Tn_CFG(i));
+
+    if ( *hpet_boot_cfg & HPET_CFG_ENABLE )
+        hpet_write32(*hpet_boot_cfg, HPET_CFG);
+}
index 77daca5ce2f0b2a15f1055f3b994f80958360ef2..fbab8d0340ef42ea1450f89e695da96ecd311318 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/mc146818rtc.h>
 #include <asm/flushtlb.h>
 #include <asm/hardirq.h>
+#include <asm/hpet.h>
 #include <asm/hvm/support.h>
 #include <mach_apic.h>
 
@@ -375,6 +376,7 @@ void smp_send_stop(void)
     local_irq_disable();
     __stop_this_cpu();
     disable_IO_APIC();
+    hpet_disable();
     local_irq_enable();
 }
 
index 91682bd0f138a25a98e000bdbb6973e16eed8817..f944e3d8f3e087def6dbd19c6546af7322185ff0 100644 (file)
@@ -377,7 +377,7 @@ static int __init init_hpet(struct platform_timesource *pts)
 
 static void resume_hpet(struct platform_timesource *pts)
 {
-    hpet_resume();
+    hpet_resume(NULL);
 }
 
 static struct platform_timesource __initdata plt_hpet =
index fe9f9b6ded1fc73997b15e58dac0a350c46fde30..bb2c4b4af8587104fe15e35647df69289be3da3c 100644 (file)
@@ -55,7 +55,12 @@ extern unsigned long hpet_address;
  * Return value is zero if HPET is unavailable.
  */
 u64 hpet_setup(void);
-void hpet_resume(void);
+void hpet_resume(u32 *);
+
+/*
+ * Disable HPET hardware: restore it to boot time state.
+ */
+void hpet_disable(void);
 
 /*
  * Callback from legacy timer (PIT channel 0) IRQ handler.