aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/hpet.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r--arch/x86/kernel/hpet.c66
1 files changed, 57 insertions, 9 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index ad0de0c2714e..9cc7b4392f7c 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -94,13 +94,18 @@ static int hpet_verbose;
94 94
95static int __init hpet_setup(char *str) 95static int __init hpet_setup(char *str)
96{ 96{
97 if (str) { 97 while (str) {
98 char *next = strchr(str, ',');
99
100 if (next)
101 *next++ = 0;
98 if (!strncmp("disable", str, 7)) 102 if (!strncmp("disable", str, 7))
99 boot_hpet_disable = 1; 103 boot_hpet_disable = 1;
100 if (!strncmp("force", str, 5)) 104 if (!strncmp("force", str, 5))
101 hpet_force_user = 1; 105 hpet_force_user = 1;
102 if (!strncmp("verbose", str, 7)) 106 if (!strncmp("verbose", str, 7))
103 hpet_verbose = 1; 107 hpet_verbose = 1;
108 str = next;
104 } 109 }
105 return 1; 110 return 1;
106} 111}
@@ -319,8 +324,6 @@ static void hpet_set_mode(enum clock_event_mode mode,
319 now = hpet_readl(HPET_COUNTER); 324 now = hpet_readl(HPET_COUNTER);
320 cmp = now + (unsigned int) delta; 325 cmp = now + (unsigned int) delta;
321 cfg = hpet_readl(HPET_Tn_CFG(timer)); 326 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 | 327 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
325 HPET_TN_SETVAL | HPET_TN_32BIT; 328 HPET_TN_SETVAL | HPET_TN_32BIT;
326 hpet_writel(cfg, HPET_Tn_CFG(timer)); 329 hpet_writel(cfg, HPET_Tn_CFG(timer));
@@ -787,15 +790,16 @@ static int hpet_clocksource_register(void)
787 return 0; 790 return 0;
788} 791}
789 792
793static u32 *hpet_boot_cfg;
794
790/** 795/**
791 * hpet_enable - Try to setup the HPET timer. Returns 1 on success. 796 * hpet_enable - Try to setup the HPET timer. Returns 1 on success.
792 */ 797 */
793int __init hpet_enable(void) 798int __init hpet_enable(void)
794{ 799{
795 unsigned long hpet_period; 800 u32 hpet_period, cfg, id;
796 unsigned int id;
797 u64 freq; 801 u64 freq;
798 int i; 802 unsigned int i, last;
799 803
800 if (!is_hpet_capable()) 804 if (!is_hpet_capable())
801 return 0; 805 return 0;
@@ -847,15 +851,45 @@ int __init hpet_enable(void)
847 id = hpet_readl(HPET_ID); 851 id = hpet_readl(HPET_ID);
848 hpet_print_config(); 852 hpet_print_config();
849 853
854 last = (id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT;
855
850#ifdef CONFIG_HPET_EMULATE_RTC 856#ifdef CONFIG_HPET_EMULATE_RTC
851 /* 857 /*
852 * The legacy routing mode needs at least two channels, tick timer 858 * The legacy routing mode needs at least two channels, tick timer
853 * and the rtc emulation channel. 859 * and the rtc emulation channel.
854 */ 860 */
855 if (!(id & HPET_ID_NUMBER)) 861 if (!last)
856 goto out_nohpet; 862 goto out_nohpet;
857#endif 863#endif
858 864
865 cfg = hpet_readl(HPET_CFG);
866 hpet_boot_cfg = kmalloc((last + 2) * sizeof(*hpet_boot_cfg),
867 GFP_KERNEL);
868 if (hpet_boot_cfg)
869 *hpet_boot_cfg = cfg;
870 else
871 pr_warn("HPET initial state will not be saved\n");
872 cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
873 hpet_writel(cfg, HPET_Tn_CFG(i));
874 if (cfg)
875 pr_warn("HPET: Unrecognized bits %#x set in global cfg\n",
876 cfg);
877
878 for (i = 0; i <= last; ++i) {
879 cfg = hpet_readl(HPET_Tn_CFG(i));
880 if (hpet_boot_cfg)
881 hpet_boot_cfg[i + 1] = cfg;
882 cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB);
883 hpet_writel(cfg, HPET_Tn_CFG(i));
884 cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP
885 | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE
886 | HPET_TN_FSB | HPET_TN_FSB_CAP);
887 if (cfg)
888 pr_warn("HPET: Unrecognized bits %#x set in cfg#%u\n",
889 cfg, i);
890 }
891 hpet_print_config();
892
859 if (hpet_clocksource_register()) 893 if (hpet_clocksource_register())
860 goto out_nohpet; 894 goto out_nohpet;
861 895
@@ -923,14 +957,28 @@ fs_initcall(hpet_late_init);
923void hpet_disable(void) 957void hpet_disable(void)
924{ 958{
925 if (is_hpet_capable() && hpet_virt_address) { 959 if (is_hpet_capable() && hpet_virt_address) {
926 unsigned int cfg = hpet_readl(HPET_CFG); 960 unsigned int cfg = hpet_readl(HPET_CFG), id, last;
927 961
928 if (hpet_legacy_int_enabled) { 962 if (hpet_boot_cfg)
963 cfg = *hpet_boot_cfg;
964 else if (hpet_legacy_int_enabled) {
929 cfg &= ~HPET_CFG_LEGACY; 965 cfg &= ~HPET_CFG_LEGACY;
930 hpet_legacy_int_enabled = 0; 966 hpet_legacy_int_enabled = 0;
931 } 967 }
932 cfg &= ~HPET_CFG_ENABLE; 968 cfg &= ~HPET_CFG_ENABLE;
933 hpet_writel(cfg, HPET_CFG); 969 hpet_writel(cfg, HPET_CFG);
970
971 if (!hpet_boot_cfg)
972 return;
973
974 id = hpet_readl(HPET_ID);
975 last = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT);
976
977 for (id = 0; id <= last; ++id)
978 hpet_writel(hpet_boot_cfg[id + 1], HPET_Tn_CFG(id));
979
980 if (*hpet_boot_cfg & HPET_CFG_ENABLE)
981 hpet_writel(*hpet_boot_cfg, HPET_CFG);
934 } 982 }
935} 983}
936 984