diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 95 |
1 files changed, 66 insertions, 29 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index dedc2bddf7a5..23b4ecdffa9b 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/sysdev.h> | 4 | #include <linux/sysdev.h> |
5 | #include <linux/delay.h> | 5 | #include <linux/delay.h> |
6 | #include <linux/errno.h> | 6 | #include <linux/errno.h> |
7 | #include <linux/slab.h> | ||
7 | #include <linux/hpet.h> | 8 | #include <linux/hpet.h> |
8 | #include <linux/init.h> | 9 | #include <linux/init.h> |
9 | #include <linux/cpu.h> | 10 | #include <linux/cpu.h> |
@@ -33,6 +34,9 @@ | |||
33 | * HPET address is set in acpi/boot.c, when an ACPI entry exists | 34 | * HPET address is set in acpi/boot.c, when an ACPI entry exists |
34 | */ | 35 | */ |
35 | unsigned long hpet_address; | 36 | unsigned long hpet_address; |
37 | u8 hpet_blockid; /* OS timer block num */ | ||
38 | u8 hpet_msi_disable; | ||
39 | |||
36 | #ifdef CONFIG_PCI_MSI | 40 | #ifdef CONFIG_PCI_MSI |
37 | static unsigned long hpet_num_timers; | 41 | static unsigned long hpet_num_timers; |
38 | #endif | 42 | #endif |
@@ -47,12 +51,12 @@ struct hpet_dev { | |||
47 | char name[10]; | 51 | char name[10]; |
48 | }; | 52 | }; |
49 | 53 | ||
50 | unsigned long hpet_readl(unsigned long a) | 54 | inline unsigned int hpet_readl(unsigned int a) |
51 | { | 55 | { |
52 | return readl(hpet_virt_address + a); | 56 | return readl(hpet_virt_address + a); |
53 | } | 57 | } |
54 | 58 | ||
55 | static inline void hpet_writel(unsigned long d, unsigned long a) | 59 | static inline void hpet_writel(unsigned int d, unsigned int a) |
56 | { | 60 | { |
57 | writel(d, hpet_virt_address + a); | 61 | writel(d, hpet_virt_address + a); |
58 | } | 62 | } |
@@ -167,7 +171,7 @@ do { \ | |||
167 | 171 | ||
168 | static void hpet_reserve_msi_timers(struct hpet_data *hd); | 172 | static void hpet_reserve_msi_timers(struct hpet_data *hd); |
169 | 173 | ||
170 | static void hpet_reserve_platform_timers(unsigned long id) | 174 | static void hpet_reserve_platform_timers(unsigned int id) |
171 | { | 175 | { |
172 | struct hpet __iomem *hpet = hpet_virt_address; | 176 | struct hpet __iomem *hpet = hpet_virt_address; |
173 | struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; | 177 | struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; |
@@ -205,7 +209,7 @@ static void hpet_reserve_platform_timers(unsigned long id) | |||
205 | 209 | ||
206 | } | 210 | } |
207 | #else | 211 | #else |
208 | static void hpet_reserve_platform_timers(unsigned long id) { } | 212 | static void hpet_reserve_platform_timers(unsigned int id) { } |
209 | #endif | 213 | #endif |
210 | 214 | ||
211 | /* | 215 | /* |
@@ -246,7 +250,7 @@ static void hpet_reset_counter(void) | |||
246 | 250 | ||
247 | static void hpet_start_counter(void) | 251 | static void hpet_start_counter(void) |
248 | { | 252 | { |
249 | unsigned long cfg = hpet_readl(HPET_CFG); | 253 | unsigned int cfg = hpet_readl(HPET_CFG); |
250 | cfg |= HPET_CFG_ENABLE; | 254 | cfg |= HPET_CFG_ENABLE; |
251 | hpet_writel(cfg, HPET_CFG); | 255 | hpet_writel(cfg, HPET_CFG); |
252 | } | 256 | } |
@@ -263,7 +267,7 @@ static void hpet_resume_device(void) | |||
263 | force_hpet_resume(); | 267 | force_hpet_resume(); |
264 | } | 268 | } |
265 | 269 | ||
266 | static void hpet_resume_counter(void) | 270 | static void hpet_resume_counter(struct clocksource *cs) |
267 | { | 271 | { |
268 | hpet_resume_device(); | 272 | hpet_resume_device(); |
269 | hpet_restart_counter(); | 273 | hpet_restart_counter(); |
@@ -271,7 +275,7 @@ static void hpet_resume_counter(void) | |||
271 | 275 | ||
272 | static void hpet_enable_legacy_int(void) | 276 | static void hpet_enable_legacy_int(void) |
273 | { | 277 | { |
274 | unsigned long cfg = hpet_readl(HPET_CFG); | 278 | unsigned int cfg = hpet_readl(HPET_CFG); |
275 | 279 | ||
276 | cfg |= HPET_CFG_LEGACY; | 280 | cfg |= HPET_CFG_LEGACY; |
277 | hpet_writel(cfg, HPET_CFG); | 281 | hpet_writel(cfg, HPET_CFG); |
@@ -314,7 +318,7 @@ static int hpet_setup_msi_irq(unsigned int irq); | |||
314 | static void hpet_set_mode(enum clock_event_mode mode, | 318 | static void hpet_set_mode(enum clock_event_mode mode, |
315 | struct clock_event_device *evt, int timer) | 319 | struct clock_event_device *evt, int timer) |
316 | { | 320 | { |
317 | unsigned long cfg, cmp, now; | 321 | unsigned int cfg, cmp, now; |
318 | uint64_t delta; | 322 | uint64_t delta; |
319 | 323 | ||
320 | switch (mode) { | 324 | switch (mode) { |
@@ -323,7 +327,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
323 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; | 327 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; |
324 | delta >>= evt->shift; | 328 | delta >>= evt->shift; |
325 | now = hpet_readl(HPET_COUNTER); | 329 | now = hpet_readl(HPET_COUNTER); |
326 | cmp = now + (unsigned long) delta; | 330 | cmp = now + (unsigned int) delta; |
327 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 331 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
328 | /* Make sure we use edge triggered interrupts */ | 332 | /* Make sure we use edge triggered interrupts */ |
329 | cfg &= ~HPET_TN_LEVEL; | 333 | cfg &= ~HPET_TN_LEVEL; |
@@ -339,7 +343,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
339 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, | 343 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, |
340 | * Publication # 24674) | 344 | * Publication # 24674) |
341 | */ | 345 | */ |
342 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); | 346 | hpet_writel((unsigned int) delta, HPET_Tn_CMP(timer)); |
343 | hpet_start_counter(); | 347 | hpet_start_counter(); |
344 | hpet_print_config(); | 348 | hpet_print_config(); |
345 | break; | 349 | break; |
@@ -383,13 +387,30 @@ static int hpet_next_event(unsigned long delta, | |||
383 | hpet_writel(cnt, HPET_Tn_CMP(timer)); | 387 | hpet_writel(cnt, HPET_Tn_CMP(timer)); |
384 | 388 | ||
385 | /* | 389 | /* |
386 | * We need to read back the CMP register to make sure that | 390 | * We need to read back the CMP register on certain HPET |
387 | * what we wrote hit the chip before we compare it to the | 391 | * implementations (ATI chipsets) which seem to delay the |
388 | * counter. | 392 | * transfer of the compare register into the internal compare |
393 | * logic. With small deltas this might actually be too late as | ||
394 | * the counter could already be higher than the compare value | ||
395 | * at that point and we would wait for the next hpet interrupt | ||
396 | * forever. We found out that reading the CMP register back | ||
397 | * forces the transfer so we can rely on the comparison with | ||
398 | * the counter register below. If the read back from the | ||
399 | * compare register does not match the value we programmed | ||
400 | * then we might have a real hardware problem. We can not do | ||
401 | * much about it here, but at least alert the user/admin with | ||
402 | * a prominent warning. | ||
403 | * An erratum on some chipsets (ICH9,..), results in comparator read | ||
404 | * immediately following a write returning old value. Workaround | ||
405 | * for this is to read this value second time, when first | ||
406 | * read returns old value. | ||
389 | */ | 407 | */ |
390 | WARN_ON_ONCE((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt); | 408 | if (unlikely((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt)) { |
409 | WARN_ONCE(hpet_readl(HPET_Tn_CMP(timer)) != cnt, | ||
410 | KERN_WARNING "hpet: compare register read back failed.\n"); | ||
411 | } | ||
391 | 412 | ||
392 | return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | 413 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; |
393 | } | 414 | } |
394 | 415 | ||
395 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | 416 | static void hpet_legacy_set_mode(enum clock_event_mode mode, |
@@ -415,7 +436,7 @@ static struct hpet_dev *hpet_devs; | |||
415 | void hpet_msi_unmask(unsigned int irq) | 436 | void hpet_msi_unmask(unsigned int irq) |
416 | { | 437 | { |
417 | struct hpet_dev *hdev = get_irq_data(irq); | 438 | struct hpet_dev *hdev = get_irq_data(irq); |
418 | unsigned long cfg; | 439 | unsigned int cfg; |
419 | 440 | ||
420 | /* unmask it */ | 441 | /* unmask it */ |
421 | cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); | 442 | cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); |
@@ -425,7 +446,7 @@ void hpet_msi_unmask(unsigned int irq) | |||
425 | 446 | ||
426 | void hpet_msi_mask(unsigned int irq) | 447 | void hpet_msi_mask(unsigned int irq) |
427 | { | 448 | { |
428 | unsigned long cfg; | 449 | unsigned int cfg; |
429 | struct hpet_dev *hdev = get_irq_data(irq); | 450 | struct hpet_dev *hdev = get_irq_data(irq); |
430 | 451 | ||
431 | /* mask it */ | 452 | /* mask it */ |
@@ -467,7 +488,7 @@ static int hpet_msi_next_event(unsigned long delta, | |||
467 | 488 | ||
468 | static int hpet_setup_msi_irq(unsigned int irq) | 489 | static int hpet_setup_msi_irq(unsigned int irq) |
469 | { | 490 | { |
470 | if (arch_setup_hpet_msi(irq)) { | 491 | if (arch_setup_hpet_msi(irq, hpet_blockid)) { |
471 | destroy_irq(irq); | 492 | destroy_irq(irq); |
472 | return -EINVAL; | 493 | return -EINVAL; |
473 | } | 494 | } |
@@ -584,6 +605,11 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
584 | unsigned int num_timers_used = 0; | 605 | unsigned int num_timers_used = 0; |
585 | int i; | 606 | int i; |
586 | 607 | ||
608 | if (hpet_msi_disable) | ||
609 | return; | ||
610 | |||
611 | if (boot_cpu_has(X86_FEATURE_ARAT)) | ||
612 | return; | ||
587 | id = hpet_readl(HPET_ID); | 613 | id = hpet_readl(HPET_ID); |
588 | 614 | ||
589 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); | 615 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
@@ -598,7 +624,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
598 | 624 | ||
599 | for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { | 625 | for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { |
600 | struct hpet_dev *hdev = &hpet_devs[num_timers_used]; | 626 | struct hpet_dev *hdev = &hpet_devs[num_timers_used]; |
601 | unsigned long cfg = hpet_readl(HPET_Tn_CFG(i)); | 627 | unsigned int cfg = hpet_readl(HPET_Tn_CFG(i)); |
602 | 628 | ||
603 | /* Only consider HPET timer with MSI support */ | 629 | /* Only consider HPET timer with MSI support */ |
604 | if (!(cfg & HPET_TN_FSB_CAP)) | 630 | if (!(cfg & HPET_TN_FSB_CAP)) |
@@ -813,7 +839,7 @@ static int hpet_clocksource_register(void) | |||
813 | */ | 839 | */ |
814 | int __init hpet_enable(void) | 840 | int __init hpet_enable(void) |
815 | { | 841 | { |
816 | unsigned long id; | 842 | unsigned int id; |
817 | int i; | 843 | int i; |
818 | 844 | ||
819 | if (!is_hpet_capable()) | 845 | if (!is_hpet_capable()) |
@@ -872,10 +898,8 @@ int __init hpet_enable(void) | |||
872 | 898 | ||
873 | if (id & HPET_ID_LEGSUP) { | 899 | if (id & HPET_ID_LEGSUP) { |
874 | hpet_legacy_clockevent_register(); | 900 | hpet_legacy_clockevent_register(); |
875 | hpet_msi_capability_lookup(2); | ||
876 | return 1; | 901 | return 1; |
877 | } | 902 | } |
878 | hpet_msi_capability_lookup(0); | ||
879 | return 0; | 903 | return 0; |
880 | 904 | ||
881 | out_nohpet: | 905 | out_nohpet: |
@@ -908,9 +932,20 @@ static __init int hpet_late_init(void) | |||
908 | if (!hpet_virt_address) | 932 | if (!hpet_virt_address) |
909 | return -ENODEV; | 933 | return -ENODEV; |
910 | 934 | ||
935 | if (hpet_readl(HPET_ID) & HPET_ID_LEGSUP) | ||
936 | hpet_msi_capability_lookup(2); | ||
937 | else | ||
938 | hpet_msi_capability_lookup(0); | ||
939 | |||
911 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); | 940 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); |
912 | hpet_print_config(); | 941 | hpet_print_config(); |
913 | 942 | ||
943 | if (hpet_msi_disable) | ||
944 | return 0; | ||
945 | |||
946 | if (boot_cpu_has(X86_FEATURE_ARAT)) | ||
947 | return 0; | ||
948 | |||
914 | for_each_online_cpu(cpu) { | 949 | for_each_online_cpu(cpu) { |
915 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); | 950 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); |
916 | } | 951 | } |
@@ -925,7 +960,7 @@ fs_initcall(hpet_late_init); | |||
925 | void hpet_disable(void) | 960 | void hpet_disable(void) |
926 | { | 961 | { |
927 | if (is_hpet_capable()) { | 962 | if (is_hpet_capable()) { |
928 | unsigned long cfg = hpet_readl(HPET_CFG); | 963 | unsigned int cfg = hpet_readl(HPET_CFG); |
929 | 964 | ||
930 | if (hpet_legacy_int_enabled) { | 965 | if (hpet_legacy_int_enabled) { |
931 | cfg &= ~HPET_CFG_LEGACY; | 966 | cfg &= ~HPET_CFG_LEGACY; |
@@ -965,8 +1000,8 @@ static int hpet_prev_update_sec; | |||
965 | static struct rtc_time hpet_alarm_time; | 1000 | static struct rtc_time hpet_alarm_time; |
966 | static unsigned long hpet_pie_count; | 1001 | static unsigned long hpet_pie_count; |
967 | static u32 hpet_t1_cmp; | 1002 | static u32 hpet_t1_cmp; |
968 | static unsigned long hpet_default_delta; | 1003 | static u32 hpet_default_delta; |
969 | static unsigned long hpet_pie_delta; | 1004 | static u32 hpet_pie_delta; |
970 | static unsigned long hpet_pie_limit; | 1005 | static unsigned long hpet_pie_limit; |
971 | 1006 | ||
972 | static rtc_irq_handler irq_handler; | 1007 | static rtc_irq_handler irq_handler; |
@@ -1017,7 +1052,8 @@ EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler); | |||
1017 | */ | 1052 | */ |
1018 | int hpet_rtc_timer_init(void) | 1053 | int hpet_rtc_timer_init(void) |
1019 | { | 1054 | { |
1020 | unsigned long cfg, cnt, delta, flags; | 1055 | unsigned int cfg, cnt, delta; |
1056 | unsigned long flags; | ||
1021 | 1057 | ||
1022 | if (!is_hpet_enabled()) | 1058 | if (!is_hpet_enabled()) |
1023 | return 0; | 1059 | return 0; |
@@ -1027,7 +1063,7 @@ int hpet_rtc_timer_init(void) | |||
1027 | 1063 | ||
1028 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; | 1064 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
1029 | clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; | 1065 | clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; |
1030 | hpet_default_delta = (unsigned long) clc; | 1066 | hpet_default_delta = clc; |
1031 | } | 1067 | } |
1032 | 1068 | ||
1033 | if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) | 1069 | if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) |
@@ -1113,7 +1149,8 @@ int hpet_set_periodic_freq(unsigned long freq) | |||
1113 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; | 1149 | clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
1114 | do_div(clc, freq); | 1150 | do_div(clc, freq); |
1115 | clc >>= hpet_clockevent.shift; | 1151 | clc >>= hpet_clockevent.shift; |
1116 | hpet_pie_delta = (unsigned long) clc; | 1152 | hpet_pie_delta = clc; |
1153 | hpet_pie_limit = 0; | ||
1117 | } | 1154 | } |
1118 | return 1; | 1155 | return 1; |
1119 | } | 1156 | } |
@@ -1127,7 +1164,7 @@ EXPORT_SYMBOL_GPL(hpet_rtc_dropped_irq); | |||
1127 | 1164 | ||
1128 | static void hpet_rtc_timer_reinit(void) | 1165 | static void hpet_rtc_timer_reinit(void) |
1129 | { | 1166 | { |
1130 | unsigned long cfg, delta; | 1167 | unsigned int cfg, delta; |
1131 | int lost_ints = -1; | 1168 | int lost_ints = -1; |
1132 | 1169 | ||
1133 | if (unlikely(!hpet_rtc_flags)) { | 1170 | if (unlikely(!hpet_rtc_flags)) { |