diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-03-26 10:45:45 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-03-26 10:45:45 -0400 |
| commit | e8684605adf87d8e65f64ce7b0274d60a05085bd (patch) | |
| tree | 7d554f19d30839a8645e0c113c4f594ef53bbffa | |
| parent | a5ebc0b1a7843508b375f7ab8a36a628e5c9f372 (diff) | |
| parent | c23e253e67c9d8a91a0ffa33c1f571a17f0a2403 (diff) | |
Merge branch 'timers/hpet' into timers/core
| -rw-r--r-- | Documentation/kernel-parameters.txt | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/hpet.c | 80 |
2 files changed, 67 insertions, 17 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..46d11a95236e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -492,10 +492,12 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 492 | Default: 64 | 492 | Default: 64 |
| 493 | 493 | ||
| 494 | hpet= [X86-32,HPET] option to control HPET usage | 494 | hpet= [X86-32,HPET] option to control HPET usage |
| 495 | Format: { enable (default) | disable | force } | 495 | Format: { enable (default) | disable | force | |
| 496 | verbose } | ||
| 496 | disable: disable HPET and use PIT instead | 497 | disable: disable HPET and use PIT instead |
| 497 | force: allow force enabled of undocumented chips (ICH4, | 498 | force: allow force enabled of undocumented chips (ICH4, |
| 498 | VIA, nVidia) | 499 | VIA, nVidia) |
| 500 | verbose: show contents of HPET registers during setup | ||
| 499 | 501 | ||
| 500 | com20020= [HW,NET] ARCnet - COM20020 chipset | 502 | com20020= [HW,NET] ARCnet - COM20020 chipset |
| 501 | Format: | 503 | Format: |
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a00545fe5cdd..648b3a2a3a44 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
| @@ -80,6 +80,7 @@ static inline void hpet_clear_mapping(void) | |||
| 80 | */ | 80 | */ |
| 81 | static int boot_hpet_disable; | 81 | static int boot_hpet_disable; |
| 82 | int hpet_force_user; | 82 | int hpet_force_user; |
| 83 | static int hpet_verbose; | ||
| 83 | 84 | ||
| 84 | static int __init hpet_setup(char *str) | 85 | static int __init hpet_setup(char *str) |
| 85 | { | 86 | { |
| @@ -88,6 +89,8 @@ static int __init hpet_setup(char *str) | |||
| 88 | boot_hpet_disable = 1; | 89 | boot_hpet_disable = 1; |
| 89 | if (!strncmp("force", str, 5)) | 90 | if (!strncmp("force", str, 5)) |
| 90 | hpet_force_user = 1; | 91 | hpet_force_user = 1; |
| 92 | if (!strncmp("verbose", str, 7)) | ||
| 93 | hpet_verbose = 1; | ||
| 91 | } | 94 | } |
| 92 | return 1; | 95 | return 1; |
| 93 | } | 96 | } |
| @@ -119,6 +122,43 @@ int is_hpet_enabled(void) | |||
| 119 | } | 122 | } |
| 120 | EXPORT_SYMBOL_GPL(is_hpet_enabled); | 123 | EXPORT_SYMBOL_GPL(is_hpet_enabled); |
| 121 | 124 | ||
| 125 | static void _hpet_print_config(const char *function, int line) | ||
| 126 | { | ||
| 127 | u32 i, timers, l, h; | ||
| 128 | printk(KERN_INFO "hpet: %s(%d):\n", function, line); | ||
| 129 | l = hpet_readl(HPET_ID); | ||
| 130 | h = hpet_readl(HPET_PERIOD); | ||
| 131 | timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; | ||
| 132 | printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); | ||
| 133 | l = hpet_readl(HPET_CFG); | ||
| 134 | h = hpet_readl(HPET_STATUS); | ||
| 135 | printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); | ||
| 136 | l = hpet_readl(HPET_COUNTER); | ||
| 137 | h = hpet_readl(HPET_COUNTER+4); | ||
| 138 | printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); | ||
| 139 | |||
| 140 | for (i = 0; i < timers; i++) { | ||
| 141 | l = hpet_readl(HPET_Tn_CFG(i)); | ||
| 142 | h = hpet_readl(HPET_Tn_CFG(i)+4); | ||
| 143 | printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", | ||
| 144 | i, l, h); | ||
| 145 | l = hpet_readl(HPET_Tn_CMP(i)); | ||
| 146 | h = hpet_readl(HPET_Tn_CMP(i)+4); | ||
| 147 | printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", | ||
| 148 | i, l, h); | ||
| 149 | l = hpet_readl(HPET_Tn_ROUTE(i)); | ||
| 150 | h = hpet_readl(HPET_Tn_ROUTE(i)+4); | ||
| 151 | printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", | ||
| 152 | i, l, h); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | #define hpet_print_config() \ | ||
| 157 | do { \ | ||
| 158 | if (hpet_verbose) \ | ||
| 159 | _hpet_print_config(__FUNCTION__, __LINE__); \ | ||
| 160 | } while (0) | ||
| 161 | |||
| 122 | /* | 162 | /* |
| 123 | * When the hpet driver (/dev/hpet) is enabled, we need to reserve | 163 | * When the hpet driver (/dev/hpet) is enabled, we need to reserve |
| 124 | * timer 0 and timer 1 in case of RTC emulation. | 164 | * timer 0 and timer 1 in case of RTC emulation. |
| @@ -191,27 +231,37 @@ static struct clock_event_device hpet_clockevent = { | |||
| 191 | .rating = 50, | 231 | .rating = 50, |
| 192 | }; | 232 | }; |
| 193 | 233 | ||
| 194 | static void hpet_start_counter(void) | 234 | static void hpet_stop_counter(void) |
| 195 | { | 235 | { |
| 196 | unsigned long cfg = hpet_readl(HPET_CFG); | 236 | unsigned long cfg = hpet_readl(HPET_CFG); |
| 197 | |||
| 198 | cfg &= ~HPET_CFG_ENABLE; | 237 | cfg &= ~HPET_CFG_ENABLE; |
| 199 | hpet_writel(cfg, HPET_CFG); | 238 | hpet_writel(cfg, HPET_CFG); |
| 200 | hpet_writel(0, HPET_COUNTER); | 239 | hpet_writel(0, HPET_COUNTER); |
| 201 | hpet_writel(0, HPET_COUNTER + 4); | 240 | hpet_writel(0, HPET_COUNTER + 4); |
| 241 | } | ||
| 242 | |||
| 243 | static void hpet_start_counter(void) | ||
| 244 | { | ||
| 245 | unsigned long cfg = hpet_readl(HPET_CFG); | ||
| 202 | cfg |= HPET_CFG_ENABLE; | 246 | cfg |= HPET_CFG_ENABLE; |
| 203 | hpet_writel(cfg, HPET_CFG); | 247 | hpet_writel(cfg, HPET_CFG); |
| 204 | } | 248 | } |
| 205 | 249 | ||
| 250 | static void hpet_restart_counter(void) | ||
| 251 | { | ||
| 252 | hpet_stop_counter(); | ||
| 253 | hpet_start_counter(); | ||
| 254 | } | ||
| 255 | |||
| 206 | static void hpet_resume_device(void) | 256 | static void hpet_resume_device(void) |
| 207 | { | 257 | { |
| 208 | force_hpet_resume(); | 258 | force_hpet_resume(); |
| 209 | } | 259 | } |
| 210 | 260 | ||
| 211 | static void hpet_restart_counter(void) | 261 | static void hpet_resume_counter(void) |
| 212 | { | 262 | { |
| 213 | hpet_resume_device(); | 263 | hpet_resume_device(); |
| 214 | hpet_start_counter(); | 264 | hpet_restart_counter(); |
| 215 | } | 265 | } |
| 216 | 266 | ||
| 217 | static void hpet_enable_legacy_int(void) | 267 | static void hpet_enable_legacy_int(void) |
| @@ -259,29 +309,23 @@ static int hpet_setup_msi_irq(unsigned int irq); | |||
| 259 | static void hpet_set_mode(enum clock_event_mode mode, | 309 | static void hpet_set_mode(enum clock_event_mode mode, |
| 260 | struct clock_event_device *evt, int timer) | 310 | struct clock_event_device *evt, int timer) |
| 261 | { | 311 | { |
| 262 | unsigned long cfg, cmp, now; | 312 | unsigned long cfg; |
| 263 | uint64_t delta; | 313 | uint64_t delta; |
| 264 | 314 | ||
| 265 | switch (mode) { | 315 | switch (mode) { |
| 266 | case CLOCK_EVT_MODE_PERIODIC: | 316 | case CLOCK_EVT_MODE_PERIODIC: |
| 317 | hpet_stop_counter(); | ||
| 267 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; | 318 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; |
| 268 | delta >>= evt->shift; | 319 | delta >>= evt->shift; |
| 269 | now = hpet_readl(HPET_COUNTER); | ||
| 270 | cmp = now + (unsigned long) delta; | ||
| 271 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 320 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
| 272 | /* Make sure we use edge triggered interrupts */ | 321 | /* Make sure we use edge triggered interrupts */ |
| 273 | cfg &= ~HPET_TN_LEVEL; | 322 | cfg &= ~HPET_TN_LEVEL; |
| 274 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | | 323 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | |
| 275 | HPET_TN_SETVAL | HPET_TN_32BIT; | 324 | HPET_TN_SETVAL | HPET_TN_32BIT; |
| 276 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | 325 | hpet_writel(cfg, HPET_Tn_CFG(timer)); |
| 277 | /* | ||
| 278 | * The first write after writing TN_SETVAL to the | ||
| 279 | * config register sets the counter value, the second | ||
| 280 | * write sets the period. | ||
| 281 | */ | ||
| 282 | hpet_writel(cmp, HPET_Tn_CMP(timer)); | ||
| 283 | udelay(1); | ||
| 284 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); | 326 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); |
| 327 | hpet_start_counter(); | ||
| 328 | hpet_print_config(); | ||
| 285 | break; | 329 | break; |
| 286 | 330 | ||
| 287 | case CLOCK_EVT_MODE_ONESHOT: | 331 | case CLOCK_EVT_MODE_ONESHOT: |
| @@ -308,6 +352,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
| 308 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); | 352 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); |
| 309 | enable_irq(hdev->irq); | 353 | enable_irq(hdev->irq); |
| 310 | } | 354 | } |
| 355 | hpet_print_config(); | ||
| 311 | break; | 356 | break; |
| 312 | } | 357 | } |
| 313 | } | 358 | } |
| @@ -526,6 +571,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
| 526 | 571 | ||
| 527 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); | 572 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
| 528 | num_timers++; /* Value read out starts from 0 */ | 573 | num_timers++; /* Value read out starts from 0 */ |
| 574 | hpet_print_config(); | ||
| 529 | 575 | ||
| 530 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); | 576 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); |
| 531 | if (!hpet_devs) | 577 | if (!hpet_devs) |
| @@ -695,7 +741,7 @@ static struct clocksource clocksource_hpet = { | |||
| 695 | .mask = HPET_MASK, | 741 | .mask = HPET_MASK, |
| 696 | .shift = HPET_SHIFT, | 742 | .shift = HPET_SHIFT, |
| 697 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 743 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 698 | .resume = hpet_restart_counter, | 744 | .resume = hpet_resume_counter, |
| 699 | #ifdef CONFIG_X86_64 | 745 | #ifdef CONFIG_X86_64 |
| 700 | .vread = vread_hpet, | 746 | .vread = vread_hpet, |
| 701 | #endif | 747 | #endif |
| @@ -707,7 +753,7 @@ static int hpet_clocksource_register(void) | |||
| 707 | cycle_t t1; | 753 | cycle_t t1; |
| 708 | 754 | ||
| 709 | /* Start the counter */ | 755 | /* Start the counter */ |
| 710 | hpet_start_counter(); | 756 | hpet_restart_counter(); |
| 711 | 757 | ||
| 712 | /* Verify whether hpet counter works */ | 758 | /* Verify whether hpet counter works */ |
| 713 | t1 = read_hpet(); | 759 | t1 = read_hpet(); |
| @@ -793,6 +839,7 @@ int __init hpet_enable(void) | |||
| 793 | * information and the number of channels | 839 | * information and the number of channels |
| 794 | */ | 840 | */ |
| 795 | id = hpet_readl(HPET_ID); | 841 | id = hpet_readl(HPET_ID); |
| 842 | hpet_print_config(); | ||
| 796 | 843 | ||
| 797 | #ifdef CONFIG_HPET_EMULATE_RTC | 844 | #ifdef CONFIG_HPET_EMULATE_RTC |
| 798 | /* | 845 | /* |
| @@ -845,6 +892,7 @@ static __init int hpet_late_init(void) | |||
| 845 | return -ENODEV; | 892 | return -ENODEV; |
| 846 | 893 | ||
| 847 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); | 894 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); |
| 895 | hpet_print_config(); | ||
| 848 | 896 | ||
| 849 | for_each_online_cpu(cpu) { | 897 | for_each_online_cpu(cpu) { |
| 850 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); | 898 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); |
