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.c95
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 */
35unsigned long hpet_address; 36unsigned long hpet_address;
37u8 hpet_blockid; /* OS timer block num */
38u8 hpet_msi_disable;
39
36#ifdef CONFIG_PCI_MSI 40#ifdef CONFIG_PCI_MSI
37static unsigned long hpet_num_timers; 41static 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
50unsigned long hpet_readl(unsigned long a) 54inline 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
55static inline void hpet_writel(unsigned long d, unsigned long a) 59static 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
168static void hpet_reserve_msi_timers(struct hpet_data *hd); 172static void hpet_reserve_msi_timers(struct hpet_data *hd);
169 173
170static void hpet_reserve_platform_timers(unsigned long id) 174static 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
208static void hpet_reserve_platform_timers(unsigned long id) { } 212static 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
247static void hpet_start_counter(void) 251static 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
266static void hpet_resume_counter(void) 270static 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
272static void hpet_enable_legacy_int(void) 276static 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);
314static void hpet_set_mode(enum clock_event_mode mode, 318static 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
395static void hpet_legacy_set_mode(enum clock_event_mode mode, 416static void hpet_legacy_set_mode(enum clock_event_mode mode,
@@ -415,7 +436,7 @@ static struct hpet_dev *hpet_devs;
415void hpet_msi_unmask(unsigned int irq) 436void 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
426void hpet_msi_mask(unsigned int irq) 447void 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
468static int hpet_setup_msi_irq(unsigned int irq) 489static 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 */
814int __init hpet_enable(void) 840int __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
881out_nohpet: 905out_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);
925void hpet_disable(void) 960void 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;
965static struct rtc_time hpet_alarm_time; 1000static struct rtc_time hpet_alarm_time;
966static unsigned long hpet_pie_count; 1001static unsigned long hpet_pie_count;
967static u32 hpet_t1_cmp; 1002static u32 hpet_t1_cmp;
968static unsigned long hpet_default_delta; 1003static u32 hpet_default_delta;
969static unsigned long hpet_pie_delta; 1004static u32 hpet_pie_delta;
970static unsigned long hpet_pie_limit; 1005static unsigned long hpet_pie_limit;
971 1006
972static rtc_irq_handler irq_handler; 1007static rtc_irq_handler irq_handler;
@@ -1017,7 +1052,8 @@ EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler);
1017 */ 1052 */
1018int hpet_rtc_timer_init(void) 1053int 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
1128static void hpet_rtc_timer_reinit(void) 1165static 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)) {