diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 59 |
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 | ||
788 | static 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 | */ |
793 | int __init hpet_enable(void) | 793 | int __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); | |||
923 | void hpet_disable(void) | 952 | void 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 | ||