Commit f36c47b4 authored by Venkatesh Pallipadi's avatar Venkatesh Pallipadi Committed by Linus Torvalds

[PATCH] HPET reenabling after suspend-resume

hpet hardware seems to need a little prodding during resume for it to start
sending the timer interupts again.  Attached patch does it for both i386
and x86_64.

Makefile change below: Right now suspend-resume ordering of system devices
depends on their order of linking.  It is ugly.  But, thats the way it
works currently.  And we want timer device to resume before PIC.
Signed-off-by: default avatar"Venkatesh Pallipadi" <venkatesh.pallipadi@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fd8ebac3
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
extra-y := head.o init_task.o vmlinux.lds extra-y := head.o init_task.o vmlinux.lds
obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \
pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \
doublefault.o quirks.o doublefault.o quirks.o
......
...@@ -321,7 +321,7 @@ unsigned long get_cmos_time(void) ...@@ -321,7 +321,7 @@ unsigned long get_cmos_time(void)
static long clock_cmos_diff, sleep_start; static long clock_cmos_diff, sleep_start;
static int time_suspend(struct sys_device *dev, u32 state) static int timer_suspend(struct sys_device *dev, u32 state)
{ {
/* /*
* Estimate time zone so that set_time can update the clock * Estimate time zone so that set_time can update the clock
...@@ -332,12 +332,18 @@ static int time_suspend(struct sys_device *dev, u32 state) ...@@ -332,12 +332,18 @@ static int time_suspend(struct sys_device *dev, u32 state)
return 0; return 0;
} }
static int time_resume(struct sys_device *dev) static int timer_resume(struct sys_device *dev)
{ {
unsigned long flags; unsigned long flags;
unsigned long sec = get_cmos_time() + clock_cmos_diff; unsigned long sec;
unsigned long sleep_length = get_cmos_time() - sleep_start; unsigned long sleep_length;
#ifdef CONFIG_HPET_TIMER
if (is_hpet_enabled())
hpet_reenable();
#endif
sec = get_cmos_time() + clock_cmos_diff;
sleep_length = get_cmos_time() - sleep_start;
write_seqlock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
xtime.tv_sec = sec; xtime.tv_sec = sec;
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
...@@ -346,24 +352,24 @@ static int time_resume(struct sys_device *dev) ...@@ -346,24 +352,24 @@ static int time_resume(struct sys_device *dev)
return 0; return 0;
} }
static struct sysdev_class pit_sysclass = { static struct sysdev_class timer_sysclass = {
.resume = time_resume, .resume = timer_resume,
.suspend = time_suspend, .suspend = timer_suspend,
set_kset_name("pit"), set_kset_name("timer"),
}; };
/* XXX this driverfs stuff should probably go elsewhere later -john */ /* XXX this driverfs stuff should probably go elsewhere later -john */
static struct sys_device device_i8253 = { static struct sys_device device_timer = {
.id = 0, .id = 0,
.cls = &pit_sysclass, .cls = &timer_sysclass,
}; };
static int time_init_device(void) static int time_init_device(void)
{ {
int error = sysdev_class_register(&pit_sysclass); int error = sysdev_class_register(&timer_sysclass);
if (!error) if (!error)
error = sysdev_register(&device_i8253); error = sysdev_register(&device_timer);
return error; return error;
} }
......
...@@ -60,13 +60,46 @@ void __init wait_hpet_tick(void) ...@@ -60,13 +60,46 @@ void __init wait_hpet_tick(void)
} }
#endif #endif
static int hpet_timer_stop_set_go(unsigned long tick)
{
unsigned int cfg;
/*
* Stop the timers and reset the main counter.
*/
cfg = hpet_readl(HPET_CFG);
cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
hpet_writel(0, HPET_COUNTER);
hpet_writel(0, HPET_COUNTER + 4);
/*
* Set up timer 0, as periodic with first interrupt to happen at
* hpet_tick, and period also hpet_tick.
*/
cfg = hpet_readl(HPET_T0_CFG);
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
HPET_TN_SETVAL | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T0_CFG);
hpet_writel(tick, HPET_T0_CMP);
/*
* Go!
*/
cfg = hpet_readl(HPET_CFG);
cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
hpet_writel(cfg, HPET_CFG);
return 0;
}
/* /*
* Check whether HPET was found by ACPI boot parse. If yes setup HPET * Check whether HPET was found by ACPI boot parse. If yes setup HPET
* counter 0 for kernel base timer. * counter 0 for kernel base timer.
*/ */
int __init hpet_enable(void) int __init hpet_enable(void)
{ {
unsigned int cfg, id; unsigned int id;
unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */ unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
unsigned long hpet_tick_rem; unsigned long hpet_tick_rem;
...@@ -108,31 +141,8 @@ int __init hpet_enable(void) ...@@ -108,31 +141,8 @@ int __init hpet_enable(void)
if (hpet_tick_rem > (hpet_period >> 1)) if (hpet_tick_rem > (hpet_period >> 1))
hpet_tick++; /* rounding the result */ hpet_tick++; /* rounding the result */
/* if (hpet_timer_stop_set_go(hpet_tick))
* Stop the timers and reset the main counter. return -1;
*/
cfg = hpet_readl(HPET_CFG);
cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
hpet_writel(0, HPET_COUNTER);
hpet_writel(0, HPET_COUNTER + 4);
/*
* Set up timer 0, as periodic with first interrupt to happen at
* hpet_tick, and period also hpet_tick.
*/
cfg = hpet_readl(HPET_T0_CFG);
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
HPET_TN_SETVAL | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T0_CFG);
hpet_writel(hpet_tick, HPET_T0_CMP);
/*
* Go!
*/
cfg = hpet_readl(HPET_CFG);
cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
hpet_writel(cfg, HPET_CFG);
use_hpet = 1; use_hpet = 1;
...@@ -185,6 +195,11 @@ int __init hpet_enable(void) ...@@ -185,6 +195,11 @@ int __init hpet_enable(void)
return 0; return 0;
} }
int hpet_reenable(void)
{
return hpet_timer_stop_set_go(hpet_tick);
}
int is_hpet_enabled(void) int is_hpet_enabled(void)
{ {
return use_hpet; return use_hpet;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
extra-y := head.o head64.o init_task.o vmlinux.lds extra-y := head.o head64.o init_task.o vmlinux.lds
EXTRA_AFLAGS := -traditional EXTRA_AFLAGS := -traditional
obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \
ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \ ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_x86_64.o \
x8664_ksyms.o i387.o syscall.o vsyscall.o \ x8664_ksyms.o i387.o syscall.o vsyscall.o \
setup64.o bootflag.o e820.o reboot.o warmreboot.o quirks.o setup64.o bootflag.o e820.o reboot.o warmreboot.o quirks.o
obj-y += mce.o obj-y += mce.o
......
...@@ -776,31 +776,9 @@ static __init int late_hpet_init(void) ...@@ -776,31 +776,9 @@ static __init int late_hpet_init(void)
fs_initcall(late_hpet_init); fs_initcall(late_hpet_init);
#endif #endif
static int hpet_init(void) static int hpet_timer_stop_set_go(unsigned long tick)
{ {
unsigned int cfg, id; unsigned int cfg;
if (!vxtime.hpet_address)
return -1;
set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address);
__set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE);
/*
* Read the period, compute tick and quotient.
*/
id = hpet_readl(HPET_ID);
if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) ||
!(id & HPET_ID_LEGSUP))
return -1;
hpet_period = hpet_readl(HPET_PERIOD);
if (hpet_period < 100000 || hpet_period > 100000000)
return -1;
hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) /
hpet_period;
/* /*
* Stop the timers and reset the main counter. * Stop the timers and reset the main counter.
...@@ -832,6 +810,40 @@ static int hpet_init(void) ...@@ -832,6 +810,40 @@ static int hpet_init(void)
return 0; return 0;
} }
static int hpet_init(void)
{
unsigned int id;
if (!vxtime.hpet_address)
return -1;
set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address);
__set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE);
/*
* Read the period, compute tick and quotient.
*/
id = hpet_readl(HPET_ID);
if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) ||
!(id & HPET_ID_LEGSUP))
return -1;
hpet_period = hpet_readl(HPET_PERIOD);
if (hpet_period < 100000 || hpet_period > 100000000)
return -1;
hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) /
hpet_period;
return hpet_timer_stop_set_go(hpet_tick);
}
static int hpet_reenable(void)
{
return hpet_timer_stop_set_go(hpet_tick);
}
void __init pit_init(void) void __init pit_init(void)
{ {
unsigned long flags; unsigned long flags;
...@@ -941,7 +953,7 @@ __setup("report_lost_ticks", time_setup); ...@@ -941,7 +953,7 @@ __setup("report_lost_ticks", time_setup);
static long clock_cmos_diff; static long clock_cmos_diff;
static int time_suspend(struct sys_device *dev, u32 state) static int timer_suspend(struct sys_device *dev, u32 state)
{ {
/* /*
* Estimate time zone so that set_time can update the clock * Estimate time zone so that set_time can update the clock
...@@ -951,10 +963,15 @@ static int time_suspend(struct sys_device *dev, u32 state) ...@@ -951,10 +963,15 @@ static int time_suspend(struct sys_device *dev, u32 state)
return 0; return 0;
} }
static int time_resume(struct sys_device *dev) static int timer_resume(struct sys_device *dev)
{ {
unsigned long flags; unsigned long flags;
unsigned long sec = get_cmos_time() + clock_cmos_diff; unsigned long sec;
if (vxtime.hpet_address)
hpet_reenable();
sec = get_cmos_time() + clock_cmos_diff;
write_seqlock_irqsave(&xtime_lock,flags); write_seqlock_irqsave(&xtime_lock,flags);
xtime.tv_sec = sec; xtime.tv_sec = sec;
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
...@@ -962,24 +979,24 @@ static int time_resume(struct sys_device *dev) ...@@ -962,24 +979,24 @@ static int time_resume(struct sys_device *dev)
return 0; return 0;
} }
static struct sysdev_class pit_sysclass = { static struct sysdev_class timer_sysclass = {
.resume = time_resume, .resume = timer_resume,
.suspend = time_suspend, .suspend = timer_suspend,
set_kset_name("pit"), set_kset_name("timer"),
}; };
/* XXX this driverfs stuff should probably go elsewhere later -john */ /* XXX this driverfs stuff should probably go elsewhere later -john */
static struct sys_device device_i8253 = { static struct sys_device device_timer = {
.id = 0, .id = 0,
.cls = &pit_sysclass, .cls = &timer_sysclass,
}; };
static int time_init_device(void) static int time_init_device(void)
{ {
int error = sysdev_class_register(&pit_sysclass); int error = sysdev_class_register(&timer_sysclass);
if (!error) if (!error)
error = sysdev_register(&device_i8253); error = sysdev_register(&device_timer);
return error; return error;
} }
......
...@@ -96,6 +96,7 @@ extern unsigned long hpet_address; /* hpet memory map physical address */ ...@@ -96,6 +96,7 @@ extern unsigned long hpet_address; /* hpet memory map physical address */
extern int hpet_rtc_timer_init(void); extern int hpet_rtc_timer_init(void);
extern int hpet_enable(void); extern int hpet_enable(void);
extern int hpet_reenable(void);
extern int is_hpet_enabled(void); extern int is_hpet_enabled(void);
extern int is_hpet_capable(void); extern int is_hpet_capable(void);
extern int hpet_readl(unsigned long a); extern int hpet_readl(unsigned long a);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment