diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 85 |
1 files changed, 57 insertions, 28 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index dedc2bddf7a5..ad80a1c718c6 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -33,6 +33,9 @@ | |||
33 | * HPET address is set in acpi/boot.c, when an ACPI entry exists | 33 | * HPET address is set in acpi/boot.c, when an ACPI entry exists |
34 | */ | 34 | */ |
35 | unsigned long hpet_address; | 35 | unsigned long hpet_address; |
36 | u8 hpet_blockid; /* OS timer block num */ | ||
37 | u8 hpet_msi_disable; | ||
38 | |||
36 | #ifdef CONFIG_PCI_MSI | 39 | #ifdef CONFIG_PCI_MSI |
37 | static unsigned long hpet_num_timers; | 40 | static unsigned long hpet_num_timers; |
38 | #endif | 41 | #endif |
@@ -47,12 +50,12 @@ struct hpet_dev { | |||
47 | char name[10]; | 50 | char name[10]; |
48 | }; | 51 | }; |
49 | 52 | ||
50 | unsigned long hpet_readl(unsigned long a) | 53 | inline unsigned int hpet_readl(unsigned int a) |
51 | { | 54 | { |
52 | return readl(hpet_virt_address + a); | 55 | return readl(hpet_virt_address + a); |
53 | } | 56 | } |
54 | 57 | ||
55 | static inline void hpet_writel(unsigned long d, unsigned long a) | 58 | static inline void hpet_writel(unsigned int d, unsigned int a) |
56 | { | 59 | { |
57 | writel(d, hpet_virt_address + a); | 60 | writel(d, hpet_virt_address + a); |
58 | } | 61 | } |
@@ -167,7 +170,7 @@ do { \ | |||
167 | 170 | ||
168 | static void hpet_reserve_msi_timers(struct hpet_data *hd); | 171 | static void hpet_reserve_msi_timers(struct hpet_data *hd); |
169 | 172 | ||
170 | static void hpet_reserve_platform_timers(unsigned long id) | 173 | static void hpet_reserve_platform_timers(unsigned int id) |
171 | { | 174 | { |
172 | struct hpet __iomem *hpet = hpet_virt_address; | 175 | struct hpet __iomem *hpet = hpet_virt_address; |
173 | struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; | 176 | struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; |
@@ -205,7 +208,7 @@ static void hpet_reserve_platform_timers(unsigned long id) | |||
205 | 208 | ||
206 | } | 209 | } |
207 | #else | 210 | #else |
208 | static void hpet_reserve_platform_timers(unsigned long id) { } | 211 | static void hpet_reserve_platform_timers(unsigned int id) { } |
209 | #endif | 212 | #endif |
210 | 213 | ||
211 | /* | 214 | /* |
@@ -246,7 +249,7 @@ static void hpet_reset_counter(void) | |||
246 | 249 | ||
247 | static void hpet_start_counter(void) | 250 | static void hpet_start_counter(void) |
248 | { | 251 | { |
249 | unsigned long cfg = hpet_readl(HPET_CFG); | 252 | unsigned int cfg = hpet_readl(HPET_CFG); |
250 | cfg |= HPET_CFG_ENABLE; | 253 | cfg |= HPET_CFG_ENABLE; |
251 | hpet_writel(cfg, HPET_CFG); | 254 | hpet_writel(cfg, HPET_CFG); |
252 | } | 255 | } |
@@ -271,7 +274,7 @@ static void hpet_resume_counter(void) | |||
271 | 274 | ||
272 | static void hpet_enable_legacy_int(void) | 275 | static void hpet_enable_legacy_int(void) |
273 | { | 276 | { |
274 | unsigned long cfg = hpet_readl(HPET_CFG); | 277 | unsigned int cfg = hpet_readl(HPET_CFG); |
275 | 278 | ||
276 | cfg |= HPET_CFG_LEGACY; | 279 | cfg |= HPET_CFG_LEGACY; |
277 | hpet_writel(cfg, HPET_CFG); | 280 | hpet_writel(cfg, HPET_CFG); |
@@ -314,7 +317,7 @@ static int hpet_setup_msi_irq(unsigned int irq); | |||
314 | static void hpet_set_mode(enum clock_event_mode mode, | 317 | static void hpet_set_mode(enum clock_event_mode mode, |
315 | struct clock_event_device *evt, int timer) | 318 | struct clock_event_device *evt, int timer) |
316 | { | 319 | { |
317 | unsigned long cfg, cmp, now; | 320 | unsigned int cfg, cmp, now; |
318 | uint64_t delta; | 321 | uint64_t delta; |
319 | 322 | ||
320 | switch (mode) { | 323 | switch (mode) { |
@@ -323,7 +326,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
323 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; | 326 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; |
324 | delta >>= evt->shift; | 327 | delta >>= evt->shift; |
325 | now = hpet_readl(HPET_COUNTER); | 328 | now = hpet_readl(HPET_COUNTER); |
326 | cmp = now + (unsigned long) delta; | 329 | cmp = now + (unsigned int) delta; |
327 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 330 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
328 | /* Make sure we use edge triggered interrupts */ | 331 | /* Make sure we use edge triggered interrupts */ |
329 | cfg &= ~HPET_TN_LEVEL; | 332 | cfg &= ~HPET_TN_LEVEL; |
@@ -339,7 +342,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
339 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, | 342 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, |
340 | * Publication # 24674) | 343 | * Publication # 24674) |
341 | */ | 344 | */ |
342 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); | 345 | hpet_writel((unsigned int) delta, HPET_Tn_CMP(timer)); |
343 | hpet_start_counter(); | 346 | hpet_start_counter(); |
344 | hpet_print_config(); | 347 | hpet_print_config(); |
345 | break; | 348 | break; |
@@ -383,13 +386,24 @@ static int hpet_next_event(unsigned long delta, | |||
383 | hpet_writel(cnt, HPET_Tn_CMP(timer)); | 386 | hpet_writel(cnt, HPET_Tn_CMP(timer)); |
384 | 387 | ||
385 | /* | 388 | /* |
386 | * We need to read back the CMP register to make sure that | 389 | * We need to read back the CMP register on certain HPET |
387 | * what we wrote hit the chip before we compare it to the | 390 | * implementations (ATI chipsets) which seem to delay the |
388 | * counter. | 391 | * transfer of the compare register into the internal compare |
392 | * logic. With small deltas this might actually be too late as | ||
393 | * the counter could already be higher than the compare value | ||
394 | * at that point and we would wait for the next hpet interrupt | ||
395 | * forever. We found out that reading the CMP register back | ||
396 | * forces the transfer so we can rely on the comparison with | ||
397 | * the counter register below. If the read back from the | ||
398 | * compare register does not match the value we programmed | ||
399 | * then we might have a real hardware problem. We can not do | ||
400 | * much about it here, but at least alert the user/admin with | ||
401 | * a prominent warning. | ||
389 | */ | 402 | */ |
390 | WARN_ON_ONCE((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt); | 403 | WARN_ONCE(hpet_readl(HPET_Tn_CMP(timer)) != cnt, |
404 | KERN_WARNING "hpet: compare register read back failed.\n"); | ||
391 | 405 | ||
392 | return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | 406 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; |
393 | } | 407 | } |
394 | 408 | ||
395 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | 409 | static void hpet_legacy_set_mode(enum clock_event_mode mode, |
@@ -415,7 +429,7 @@ static struct hpet_dev *hpet_devs; | |||
415 | void hpet_msi_unmask(unsigned int irq) | 429 | void hpet_msi_unmask(unsigned int irq) |
416 | { | 430 | { |
417 | struct hpet_dev *hdev = get_irq_data(irq); | 431 | struct hpet_dev *hdev = get_irq_data(irq); |
418 | unsigned long cfg; | 432 | unsigned int cfg; |
419 | 433 | ||
420 | /* unmask it */ | 434 | /* unmask it */ |
421 | cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); | 435 | cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); |
@@ -425,7 +439,7 @@ void hpet_msi_unmask(unsigned int irq) | |||
425 | 439 | ||
426 | void hpet_msi_mask(unsigned int irq) | 440 | void hpet_msi_mask(unsigned int irq) |
427 | { | 441 | { |
428 | unsigned long cfg; | 442 | unsigned int cfg; |
429 | struct hpet_dev *hdev = get_irq_data(irq); | 443 | struct hpet_dev *hdev = get_irq_data(irq); |
430 | 444 | ||
431 | /* mask it */ | 445 | /* mask it */ |
@@ -467,7 +481,7 @@ static int hpet_msi_next_event(unsigned long delta, | |||
467 | 481 | ||
468 | static int hpet_setup_msi_irq(unsigned int irq) | 482 | static int hpet_setup_msi_irq(unsigned int irq) |
469 | { | 483 | { |
470 | if (arch_setup_hpet_msi(irq)) { | 484 | if (arch_setup_hpet_msi(irq, hpet_blockid)) { |
471 | destroy_irq(irq); | 485 | destroy_irq(irq); |
472 | return -EINVAL; | 486 | return -EINVAL; |
473 | } | 487 | } |
@@ -584,6 +598,11 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
584 | unsigned int num_timers_used = 0; | 598 | unsigned int num_timers_used = 0; |
585 | int i; | 599 | int i; |
586 | 600 | ||
601 | if (hpet_msi_disable) | ||
602 | return; | ||
603 | |||
604 | if (boot_cpu_has(X86_FEATURE_ARAT)) | ||
605 | return; | ||
587 | id = hpet_readl(HPET_ID); | 606 | id = hpet_readl(HPET_ID); |
588 | 607 | ||
589 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); | 608 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
@@ -598,7 +617,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
598 | 617 | ||
599 | for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { | 618 | for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { |
600 | struct hpet_dev *hdev = &hpet_devs[num_timers_used]; | 619 | struct hpet_dev *hdev = &hpet_devs[num_timers_used]; |
601 | unsigned long cfg = hpet_readl(HPET_Tn_CFG(i)); | 620 | unsigned int cfg = hpet_readl(HPET_Tn_CFG(i)); |
602 | 621 | ||
603 | /* Only consider HPET timer with MSI support */ | 622 | /* Only consider HPET timer with MSI support */ |
604 | if (!(cfg & HPET_TN_FSB_CAP)) | 623 | if (!(cfg & HPET_TN_FSB_CAP)) |
@@ -813,7 +832,7 @@ static int hpet_clocksource_register(void) | |||
813 | */ | 832 | */ |
814 | int __init hpet_enable(void) | 833 | int __init hpet_enable(void) |
815 | { | 834 | { |
816 | unsigned long id; | 835 | unsigned int id; |
817 | int i; | 836 | int i; |
818 | 837 | ||
819 | if (!is_hpet_capable()) | 838 | if (!is_hpet_capable()) |
@@ -872,10 +891,8 @@ int __init hpet_enable(void) | |||
872 | 891 | ||
873 | if (id & HPET_ID_LEGSUP) { | 892 | if (id & HPET_ID_LEGSUP) { |
874 | hpet_legacy_clockevent_register(); | 893 | hpet_legacy_clockevent_register(); |
875 | hpet_msi_capability_lookup(2); | ||
876 | return 1; | 894 | return 1; |
877 | } | 895 | } |
878 | hpet_msi_capability_lookup(0); | ||
879 | return 0; | 896 | return 0; |
880 | 897 | ||
881 | out_nohpet: | 898 | out_nohpet: |
@@ -908,9 +925,20 @@ static __init int hpet_late_init(void) | |||
908 | if (!hpet_virt_address) | 925 | if (!hpet_virt_address) |
909 | return -ENODEV; | 926 | return -ENODEV; |
910 | 927 | ||
928 | if (hpet_readl(HPET_ID) & HPET_ID_LEGSUP) | ||
929 | hpet_msi_capability_lookup(2); | ||
930 | else | ||
931 | hpet_msi_capability_lookup(0); | ||
932 | |||
911 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); | 933 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); |
912 | hpet_print_config(); | 934 | hpet_print_config(); |
913 | 935 | ||
936 | if (hpet_msi_disable) | ||
937 | return 0; | ||
938 | |||
939 | if (boot_cpu_has(X86_FEATURE_ARAT)) | ||
940 | return 0; | ||
941 | |||
914 | for_each_online_cpu(cpu) { | 942 | for_each_online_cpu(cpu) { |
915 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); | 943 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); |
916 | } | 944 | } |
@@ -925,7 +953,7 @@ fs_initcall(hpet_late_init); | |||
925 | void hpet_disable(void) | 953 | void hpet_disable(void) |
926 | { | 954 | { |
927 | if (is_hpet_capable()) { | 955 | if (is_hpet_capable()) { |
928 | unsigned long cfg = hpet_readl(HPET_CFG); | 956 | unsigned int cfg = hpet_readl(HPET_CFG); |
929 | 957 | ||
930 | if (hpet_legacy_int_enabled) { | 958 | if (hpet_legacy_int_enabled) { |
931 | cfg &= ~HPET_CFG_LEGACY; | 959 | cfg &= ~HPET_CFG_LEGACY; |
@@ -965,8 +993,8 @@ static int hpet_prev_update_sec; | |||
965 | static struct rtc_time hpet_alarm_time; | 993 | static struct rtc_time hpet_alarm_time; |
966 | static unsigned long hpet_pie_count; | 994 | static unsigned long hpet_pie_count; |
967 | static u32 hpet_t1_cmp; | 995 | static u32 hpet_t1_cmp; |
968 | static unsigned long hpet_default_delta; | 996 | static u32 hpet_default_delta; |
969 | static unsigned long hpet_pie_delta; | 997 | static u32 hpet_pie_delta; |
970 | static unsigned long hpet_pie_limit; | 998 | static unsigned long hpet_pie_limit; |
971 | 999 | ||
972 | static rtc_irq_handler irq_handler; | 1000 | static rtc_irq_handler irq_handler; |
@@ -1017,7 +1045,8 @@ EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler); | |||
1017 | */ | 1045 | */ |
1018 | int hpet_rtc_timer_init(void) | 1046 | int hpet_rtc_timer_init(void) |
1019 | { | 1047 | { |
1020 | unsigned long cfg, cnt, delta, flags; | 1048 | unsigned int cfg, cnt, delta; |
1049 | unsigned long flags; | ||
1021 | 1050 | ||
1022 | if (!is_hpet_enabled()) | 1051 | if (!is_hpet_enabled()) |
1023 | return 0; | 1052 | return 0; |
@@ -1027,7 +1056,7 @@ int hpet_rtc_timer_init(void) | |||
1027 | 1056 | ||
1028 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; | 1057 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
1029 | clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; | 1058 | clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; |
1030 | hpet_default_delta = (unsigned long) clc; | 1059 | hpet_default_delta = clc; |
1031 | } | 1060 | } |
1032 | 1061 | ||
1033 | if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) | 1062 | if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) |
@@ -1113,7 +1142,7 @@ int hpet_set_periodic_freq(unsigned long freq) | |||
1113 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; | 1142 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
1114 | do_div(clc, freq); | 1143 | do_div(clc, freq); |
1115 | clc >>= hpet_clockevent.shift; | 1144 | clc >>= hpet_clockevent.shift; |
1116 | hpet_pie_delta = (unsigned long) clc; | 1145 | hpet_pie_delta = clc; |
1117 | } | 1146 | } |
1118 | return 1; | 1147 | return 1; |
1119 | } | 1148 | } |
@@ -1127,7 +1156,7 @@ EXPORT_SYMBOL_GPL(hpet_rtc_dropped_irq); | |||
1127 | 1156 | ||
1128 | static void hpet_rtc_timer_reinit(void) | 1157 | static void hpet_rtc_timer_reinit(void) |
1129 | { | 1158 | { |
1130 | unsigned long cfg, delta; | 1159 | unsigned int cfg, delta; |
1131 | int lost_ints = -1; | 1160 | int lost_ints = -1; |
1132 | 1161 | ||
1133 | if (unlikely(!hpet_rtc_flags)) { | 1162 | if (unlikely(!hpet_rtc_flags)) { |