diff options
| author | Maxim Levitsky <maximlevitsky@gmail.com> | 2007-03-29 09:46:48 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-29 13:25:32 -0400 |
| commit | 399afa4fc9238fbae42116cf25a54671c0e8f56e (patch) | |
| tree | 875b69d487a1ee858cf1c871649627e213bc1019 | |
| parent | 22c8c65d24b37cef1d28583c7a7ffbf84f840e7a (diff) | |
[PATCH] Add suspend/resume for HPET
This adds support of suspend/resume on i386 for HPET, which fixes a
number of timer-related failures around STR.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
Acked-by: Michael S. Tsirkin <mst@dev.mellanox.co.il>
Acked-by: Jeff Chua <jeff.chua.linux@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | arch/i386/kernel/hpet.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c index 76afea67f691..17d73459fc5f 100644 --- a/arch/i386/kernel/hpet.c +++ b/arch/i386/kernel/hpet.c | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | #include <linux/errno.h> | 3 | #include <linux/errno.h> |
| 4 | #include <linux/hpet.h> | 4 | #include <linux/hpet.h> |
| 5 | #include <linux/init.h> | 5 | #include <linux/init.h> |
| 6 | #include <linux/sysdev.h> | ||
| 7 | #include <linux/pm.h> | ||
| 6 | 8 | ||
| 7 | #include <asm/hpet.h> | 9 | #include <asm/hpet.h> |
| 8 | #include <asm/io.h> | 10 | #include <asm/io.h> |
| @@ -307,6 +309,7 @@ int __init hpet_enable(void) | |||
| 307 | out_nohpet: | 309 | out_nohpet: |
| 308 | iounmap(hpet_virt_address); | 310 | iounmap(hpet_virt_address); |
| 309 | hpet_virt_address = NULL; | 311 | hpet_virt_address = NULL; |
| 312 | boot_hpet_disable = 1; | ||
| 310 | return 0; | 313 | return 0; |
| 311 | } | 314 | } |
| 312 | 315 | ||
| @@ -521,3 +524,68 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) | |||
| 521 | return IRQ_HANDLED; | 524 | return IRQ_HANDLED; |
| 522 | } | 525 | } |
| 523 | #endif | 526 | #endif |
| 527 | |||
| 528 | |||
| 529 | /* | ||
| 530 | * Suspend/resume part | ||
| 531 | */ | ||
| 532 | |||
| 533 | #ifdef CONFIG_PM | ||
| 534 | |||
| 535 | static int hpet_suspend(struct sys_device *sys_device, pm_message_t state) | ||
| 536 | { | ||
| 537 | unsigned long cfg = hpet_readl(HPET_CFG); | ||
| 538 | |||
| 539 | cfg &= ~(HPET_CFG_ENABLE|HPET_CFG_LEGACY); | ||
| 540 | hpet_writel(cfg, HPET_CFG); | ||
| 541 | |||
| 542 | return 0; | ||
| 543 | } | ||
| 544 | |||
| 545 | static int hpet_resume(struct sys_device *sys_device) | ||
| 546 | { | ||
| 547 | unsigned int id; | ||
| 548 | |||
| 549 | hpet_start_counter(); | ||
| 550 | |||
| 551 | id = hpet_readl(HPET_ID); | ||
| 552 | |||
| 553 | if (id & HPET_ID_LEGSUP) | ||
| 554 | hpet_enable_int(); | ||
| 555 | |||
| 556 | return 0; | ||
| 557 | } | ||
| 558 | |||
| 559 | static struct sysdev_class hpet_class = { | ||
| 560 | set_kset_name("hpet"), | ||
| 561 | .suspend = hpet_suspend, | ||
| 562 | .resume = hpet_resume, | ||
| 563 | }; | ||
| 564 | |||
| 565 | static struct sys_device hpet_device = { | ||
| 566 | .id = 0, | ||
| 567 | .cls = &hpet_class, | ||
| 568 | }; | ||
| 569 | |||
| 570 | |||
| 571 | static __init int hpet_register_sysfs(void) | ||
| 572 | { | ||
| 573 | int err; | ||
| 574 | |||
| 575 | if (!is_hpet_capable()) | ||
| 576 | return 0; | ||
| 577 | |||
| 578 | err = sysdev_class_register(&hpet_class); | ||
| 579 | |||
| 580 | if (!err) { | ||
| 581 | err = sysdev_register(&hpet_device); | ||
| 582 | if (err) | ||
| 583 | sysdev_class_unregister(&hpet_class); | ||
| 584 | } | ||
| 585 | |||
| 586 | return err; | ||
| 587 | } | ||
| 588 | |||
| 589 | device_initcall(hpet_register_sysfs); | ||
| 590 | |||
| 591 | #endif | ||
