aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorJan Beulich <JBeulich@suse.com>2012-04-02 10:15:55 -0400
committerIngo Molnar <mingo@kernel.org>2012-05-07 09:06:27 -0400
commit396e2c6fed4ff13b53ce0e573105531cf53b0cad (patch)
tree77449d826523f8bbe62fa7f733095e6544547cfe /arch/x86/kernel
parentfebb72a6e4cc6c8cffcc1ea649a3fb364f1ea432 (diff)
x86: Clear HPET configuration registers on startup
While Linux itself has been calling hpet_disable() for quite a while, having e.g. a secondary (kexec) kernel depend on such behavior of the primary (crashed) environment is fragile. It particularly broke until very recently when the primary environment was Xen based, as that hypervisor did not clear any of the HPET settings it may have used. Rather than blindly (and incompletely) clearing certain HPET settings in hpet_disable(), latch the config register settings during boot and restore then here. (Note on the hpet_set_mode() change: Now that we're clearing the level bit upon initialization, there's no need anymore to do so here.) Signed-off-by: Jan Beulich <jbeulich@suse.com> Link: http://lkml.kernel.org/r/4F79D0BB020000780007C02D@nat28.tlf.novell.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/hpet.c59
1 files changed, 51 insertions, 8 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index ad0de0c2714..70bce5db1bb 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -319,8 +319,6 @@ static void hpet_set_mode(enum clock_event_mode mode,
319 now = hpet_readl(HPET_COUNTER); 319 now = hpet_readl(HPET_COUNTER);
320 cmp = now + (unsigned int) delta; 320 cmp = now + (unsigned int) delta;
321 cfg = hpet_readl(HPET_Tn_CFG(timer)); 321 cfg = hpet_readl(HPET_Tn_CFG(timer));
322 /* Make sure we use edge triggered interrupts */
323 cfg &= ~HPET_TN_LEVEL;
324 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | 322 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
325 HPET_TN_SETVAL | HPET_TN_32BIT; 323 HPET_TN_SETVAL | HPET_TN_32BIT;
326 hpet_writel(cfg, HPET_Tn_CFG(timer)); 324 hpet_writel(cfg, HPET_Tn_CFG(timer));
@@ -787,15 +785,16 @@ static int hpet_clocksource_register(void)
787 return 0; 785 return 0;
788} 786}
789 787
788static u32 *hpet_boot_cfg;
789
790/** 790/**
791 * hpet_enable - Try to setup the HPET timer. Returns 1 on success. 791 * hpet_enable - Try to setup the HPET timer. Returns 1 on success.
792 */ 792 */
793int __init hpet_enable(void) 793int __init hpet_enable(void)
794{ 794{
795 unsigned long hpet_period; 795 u32 hpet_period, cfg, id;
796 unsigned int id;
797 u64 freq; 796 u64 freq;
798 int i; 797 unsigned int i, last;
799 798
800 if (!is_hpet_capable()) 799 if (!is_hpet_capable())
801 return 0; 800 return 0;
@@ -847,15 +846,45 @@ int __init hpet_enable(void)
847 id = hpet_readl(HPET_ID); 846 id = hpet_readl(HPET_ID);
848 hpet_print_config(); 847 hpet_print_config();
849 848
849 last = (id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
850
850#ifdef CONFIG_HPET_EMULATE_RTC 851#ifdef CONFIG_HPET_EMULATE_RTC
851 /* 852 /*
852 * The legacy routing mode needs at least two channels, tick timer 853 * The legacy routing mode needs at least two channels, tick timer
853 * and the rtc emulation channel. 854 * and the rtc emulation channel.
854 */ 855 */
855 if (!(id & HPET_ID_NUMBER)) 856 if (!last)
856 goto out_nohpet; 857 goto out_nohpet;
857#endif 858#endif
858 859
860 cfg = hpet_readl(HPET_CFG);
861 hpet_boot_cfg = kmalloc((last + 2) * sizeof(*hpet_boot_cfg),
862 GFP_KERNEL);
863 if (hpet_boot_cfg)
864 *hpet_boot_cfg = cfg;
865 else
866 pr_warn("HPET initial state will not be saved\n");
867 cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
868 hpet_writel(cfg, HPET_Tn_CFG(i));
869 if (cfg)
870 pr_warn("HPET: Unrecognized bits %#x set in global cfg\n",
871 cfg);
872
873 for (i = 0; i <= last; ++i) {
874 cfg = hpet_readl(HPET_Tn_CFG(i));
875 if (hpet_boot_cfg)
876 hpet_boot_cfg[i + 1] = cfg;
877 cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB);
878 hpet_writel(cfg, HPET_Tn_CFG(i));
879 cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP
880 | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE
881 | HPET_TN_FSB | HPET_TN_FSB_CAP);
882 if (cfg)
883 pr_warn("HPET: Unrecognized bits %#x set in cfg#%u\n",
884 cfg, i);
885 }
886 hpet_print_config();
887
859 if (hpet_clocksource_register()) 888 if (hpet_clocksource_register())
860 goto out_nohpet; 889 goto out_nohpet;
861 890
@@ -923,14 +952,28 @@ fs_initcall(hpet_late_init);
923void hpet_disable(void) 952void hpet_disable(void)
924{ 953{
925 if (is_hpet_capable() && hpet_virt_address) { 954 if (is_hpet_capable() && hpet_virt_address) {
926 unsigned int cfg = hpet_readl(HPET_CFG); 955 unsigned int cfg = hpet_readl(HPET_CFG), id, last;
927 956
928 if (hpet_legacy_int_enabled) { 957 if (hpet_boot_cfg)
958 cfg = *hpet_boot_cfg;
959 else if (hpet_legacy_int_enabled) {
929 cfg &= ~HPET_CFG_LEGACY; 960 cfg &= ~HPET_CFG_LEGACY;
930 hpet_legacy_int_enabled = 0; 961 hpet_legacy_int_enabled = 0;
931 } 962 }
932 cfg &= ~HPET_CFG_ENABLE; 963 cfg &= ~HPET_CFG_ENABLE;
933 hpet_writel(cfg, HPET_CFG); 964 hpet_writel(cfg, HPET_CFG);
965
966 if (!hpet_boot_cfg)
967 return;
968
969 id = hpet_readl(HPET_ID);
970 last = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT);
971
972 for (id = 0; id <= last; ++id)
973 hpet_writel(hpet_boot_cfg[id + 1], HPET_Tn_CFG(id));
974
975 if (*hpet_boot_cfg & HPET_CFG_ENABLE)
976 hpet_writel(*hpet_boot_cfg, HPET_CFG);
934 } 977 }
935} 978}
936 979