Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3c433e4 authored by Shaohua Li's avatar Shaohua Li Committed by Linus Torvalds
Browse files

[PATCH] add suspend/resume for timer



The timers lack .suspend/.resume methods.  Because of this, jiffies got a
big compensation after a S3 resume.  And then softlockup watchdog reports
an oops.  This occured with HPET enabled, but it's also possible for other
timers.

Signed-off-by: default avatarShaohua Li <shaohua.li@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 57c4ce3c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -383,6 +383,7 @@ void notify_arch_cmos_timer(void)

static long clock_cmos_diff, sleep_start;

static struct timer_opts *last_timer;
static int timer_suspend(struct sys_device *dev, pm_message_t state)
{
	/*
@@ -391,6 +392,10 @@ static int timer_suspend(struct sys_device *dev, pm_message_t state)
	clock_cmos_diff = -get_cmos_time();
	clock_cmos_diff += get_seconds();
	sleep_start = get_cmos_time();
	last_timer = cur_timer;
	cur_timer = &timer_none;
	if (last_timer->suspend)
		last_timer->suspend(state);
	return 0;
}

@@ -404,6 +409,7 @@ static int timer_resume(struct sys_device *dev)
	if (is_hpet_enabled())
		hpet_reenable();
#endif
	setup_pit_timer();
	sec = get_cmos_time() + clock_cmos_diff;
	sleep_length = (get_cmos_time() - sleep_start) * HZ;
	write_seqlock_irqsave(&xtime_lock, flags);
@@ -412,6 +418,10 @@ static int timer_resume(struct sys_device *dev)
	write_sequnlock_irqrestore(&xtime_lock, flags);
	jiffies += sleep_length;
	wall_jiffies += sleep_length;
	if (last_timer->resume)
		last_timer->resume();
	cur_timer = last_timer;
	last_timer = NULL;
	return 0;
}

+14 −0
Original line number Diff line number Diff line
@@ -181,6 +181,19 @@ static int __init init_hpet(char* override)
	return 0;
}

static int hpet_resume(void)
{
	write_seqlock(&monotonic_lock);
	/* Assume this is the last mark offset time */
	rdtsc(last_tsc_low, last_tsc_high);

	if (hpet_use_timer)
		hpet_last = hpet_readl(HPET_T0_CMP) - hpet_tick;
	else
		hpet_last = hpet_readl(HPET_COUNTER);
	write_sequnlock(&monotonic_lock);
	return 0;
}
/************************************************************/

/* tsc timer_opts struct */
@@ -190,6 +203,7 @@ static struct timer_opts timer_hpet __read_mostly = {
	.get_offset =		get_offset_hpet,
	.monotonic_clock =	monotonic_clock_hpet,
	.delay = 		delay_hpet,
	.resume	=		hpet_resume,
};

struct init_timer_opts __initdata timer_hpet_init = {
+0 −27
Original line number Diff line number Diff line
@@ -175,30 +175,3 @@ void setup_pit_timer(void)
	outb(LATCH >> 8 , PIT_CH0);	/* MSB */
	spin_unlock_irqrestore(&i8253_lock, flags);
}

static int timer_resume(struct sys_device *dev)
{
	setup_pit_timer();
	return 0;
}

static struct sysdev_class timer_sysclass = {
	set_kset_name("timer_pit"),
	.resume	= timer_resume,
};

static struct sys_device device_timer = {
	.id	= 0,
	.cls	= &timer_sysclass,
};

static int __init init_timer_sysfs(void)
{
	int error = sysdev_class_register(&timer_sysclass);
	if (!error)
		error = sysdev_register(&device_timer);
	return error;
}

device_initcall(init_timer_sysfs);
+9 −0
Original line number Diff line number Diff line
@@ -186,6 +186,14 @@ static void mark_offset_pmtmr(void)
	}
}

static int pmtmr_resume(void)
{
	write_seqlock(&monotonic_lock);
	/* Assume this is the last mark offset time */
	offset_tick = read_pmtmr();
	write_sequnlock(&monotonic_lock);
	return 0;
}

static unsigned long long monotonic_clock_pmtmr(void)
{
@@ -247,6 +255,7 @@ static struct timer_opts timer_pmtmr = {
	.monotonic_clock 	= monotonic_clock_pmtmr,
	.delay 			= delay_pmtmr,
	.read_timer 		= read_timer_tsc,
	.resume			= pmtmr_resume,
};

struct init_timer_opts __initdata timer_pmtmr_init = {
+14 −0
Original line number Diff line number Diff line
@@ -543,6 +543,19 @@ static int __init init_tsc(char* override)
	return -ENODEV;
}

static int tsc_resume(void)
{
	write_seqlock(&monotonic_lock);
	/* Assume this is the last mark offset time */
	rdtsc(last_tsc_low, last_tsc_high);
#ifdef CONFIG_HPET_TIMER
	if (is_hpet_enabled() && hpet_use_timer)
		hpet_last = hpet_readl(HPET_COUNTER);
#endif
	write_sequnlock(&monotonic_lock);
	return 0;
}

#ifndef CONFIG_X86_TSC
/* disable flag for tsc.  Takes effect by clearing the TSC cpu flag
 * in cpu/common.c */
@@ -573,6 +586,7 @@ static struct timer_opts timer_tsc = {
	.monotonic_clock = monotonic_clock_tsc,
	.delay = delay_tsc,
	.read_timer = read_timer_tsc,
	.resume	= tsc_resume,
};

struct init_timer_opts __initdata timer_tsc_init = {
Loading