diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 66 |
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 | ||
95 | static int __init hpet_setup(char *str) | 95 | static 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 | ||
793 | static 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 | */ |
793 | int __init hpet_enable(void) | 798 | int __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); | |||
923 | void hpet_disable(void) | 957 | void 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 | ||