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

Commit 1447c27d authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Linus Torvalds
Browse files

[PATCH] hpet rtc emulation: add watchdog timer



To prevent the emulated RTC timer from stopping when interrupts are delayed
for too long, disable interrupts around all of the register initialization,
and check that the interrupt handler did not schedule the next interrupt in
the past.

Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Cc: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Cc: Andi Kleen <ak@muc.de>
Cc: Vojtech Pavlik <vojtech@suse.cz>
Cc: Robert Picco <Robert.Picco@hp.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2514183d
Loading
Loading
Loading
Loading
+31 −6
Original line number Original line Diff line number Diff line
@@ -301,23 +301,25 @@ int hpet_rtc_timer_init(void)
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;


	local_irq_save(flags);
	local_irq_save(flags);

	cnt = hpet_readl(HPET_COUNTER);
	cnt = hpet_readl(HPET_COUNTER);
	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_t1_cmp = cnt;
	hpet_t1_cmp = cnt;
	local_irq_restore(flags);


	cfg = hpet_readl(HPET_T1_CFG);
	cfg = hpet_readl(HPET_T1_CFG);
	cfg &= ~HPET_TN_PERIODIC;
	cfg &= ~HPET_TN_PERIODIC;
	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
	hpet_writel(cfg, HPET_T1_CFG);
	hpet_writel(cfg, HPET_T1_CFG);


	local_irq_restore(flags);

	return 1;
	return 1;
}
}


static void hpet_rtc_timer_reinit(void)
static void hpet_rtc_timer_reinit(void)
{
{
	unsigned int cfg, cnt;
	unsigned int cfg, cnt, ticks_per_int, lost_ints;


	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
		cfg = hpet_readl(HPET_T1_CFG);
		cfg = hpet_readl(HPET_T1_CFG);
@@ -332,10 +334,33 @@ static void hpet_rtc_timer_reinit(void)
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;


	/* It is more accurate to use the comparator value than current count.*/
	/* It is more accurate to use the comparator value than current count.*/
	cnt = hpet_t1_cmp;
	ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
	cnt += hpet_tick*HZ/hpet_rtc_int_freq;
	hpet_t1_cmp += ticks_per_int;
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
	hpet_t1_cmp = cnt;

	/*
	 * If the interrupt handler was delayed too long, the write above tries
	 * to schedule the next interrupt in the past and the hardware would
	 * not interrupt until the counter had wrapped around.
	 * So we have to check that the comparator wasn't set to a past time.
	 */
	cnt = hpet_readl(HPET_COUNTER);
	if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
		lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
		/* Make sure that, even with the time needed to execute
		 * this code, the next scheduled interrupt has been moved
		 * back to the future: */
		lost_ints++;

		hpet_t1_cmp += lost_ints * ticks_per_int;
		hpet_writel(hpet_t1_cmp, HPET_T1_CMP);

		if (PIE_on)
			PIE_count += lost_ints;

		printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
		       hpet_rtc_int_freq);
	}
}
}


/*
/*
+31 −6
Original line number Original line Diff line number Diff line
@@ -1148,23 +1148,25 @@ int hpet_rtc_timer_init(void)
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;


	local_irq_save(flags);
	local_irq_save(flags);

	cnt = hpet_readl(HPET_COUNTER);
	cnt = hpet_readl(HPET_COUNTER);
	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_t1_cmp = cnt;
	hpet_t1_cmp = cnt;
	local_irq_restore(flags);


	cfg = hpet_readl(HPET_T1_CFG);
	cfg = hpet_readl(HPET_T1_CFG);
	cfg &= ~HPET_TN_PERIODIC;
	cfg &= ~HPET_TN_PERIODIC;
	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
	hpet_writel(cfg, HPET_T1_CFG);
	hpet_writel(cfg, HPET_T1_CFG);


	local_irq_restore(flags);

	return 1;
	return 1;
}
}


static void hpet_rtc_timer_reinit(void)
static void hpet_rtc_timer_reinit(void)
{
{
	unsigned int cfg, cnt;
	unsigned int cfg, cnt, ticks_per_int, lost_ints;


	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
	if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
		cfg = hpet_readl(HPET_T1_CFG);
		cfg = hpet_readl(HPET_T1_CFG);
@@ -1179,10 +1181,33 @@ static void hpet_rtc_timer_reinit(void)
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;


	/* It is more accurate to use the comparator value than current count.*/
	/* It is more accurate to use the comparator value than current count.*/
	cnt = hpet_t1_cmp;
	ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
	cnt += hpet_tick*HZ/hpet_rtc_int_freq;
	hpet_t1_cmp += ticks_per_int;
	hpet_writel(cnt, HPET_T1_CMP);
	hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
	hpet_t1_cmp = cnt;

	/*
	 * If the interrupt handler was delayed too long, the write above tries
	 * to schedule the next interrupt in the past and the hardware would
	 * not interrupt until the counter had wrapped around.
	 * So we have to check that the comparator wasn't set to a past time.
	 */
	cnt = hpet_readl(HPET_COUNTER);
	if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
		lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
		/* Make sure that, even with the time needed to execute
		 * this code, the next scheduled interrupt has been moved
		 * back to the future: */
		lost_ints++;

		hpet_t1_cmp += lost_ints * ticks_per_int;
		hpet_writel(hpet_t1_cmp, HPET_T1_CMP);

		if (PIE_on)
			PIE_count += lost_ints;

		printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
		       hpet_rtc_int_freq);
	}
}
}


/*
/*