aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2006-09-26 02:32:17 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-26 11:48:54 -0400
commit1447c27d38faf8fb03d4599e8082e507453ea3cf (patch)
tree80134676ddcc63bdfbba86978a7a2d6b2d537e87 /arch/x86_64
parent2514183dff2d5282cb745af34f56d1b98e5b2df8 (diff)
[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: Clemens 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: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64')
-rw-r--r--arch/x86_64/kernel/time.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 7a9b18224182..7700e6cd2bd9 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -1148,23 +1148,25 @@ int hpet_rtc_timer_init(void)
1148 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; 1148 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
1149 1149
1150 local_irq_save(flags); 1150 local_irq_save(flags);
1151
1151 cnt = hpet_readl(HPET_COUNTER); 1152 cnt = hpet_readl(HPET_COUNTER);
1152 cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq); 1153 cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
1153 hpet_writel(cnt, HPET_T1_CMP); 1154 hpet_writel(cnt, HPET_T1_CMP);
1154 hpet_t1_cmp = cnt; 1155 hpet_t1_cmp = cnt;
1155 local_irq_restore(flags);
1156 1156
1157 cfg = hpet_readl(HPET_T1_CFG); 1157 cfg = hpet_readl(HPET_T1_CFG);
1158 cfg &= ~HPET_TN_PERIODIC; 1158 cfg &= ~HPET_TN_PERIODIC;
1159 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; 1159 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
1160 hpet_writel(cfg, HPET_T1_CFG); 1160 hpet_writel(cfg, HPET_T1_CFG);
1161 1161
1162 local_irq_restore(flags);
1163
1162 return 1; 1164 return 1;
1163} 1165}
1164 1166
1165static void hpet_rtc_timer_reinit(void) 1167static void hpet_rtc_timer_reinit(void)
1166{ 1168{
1167 unsigned int cfg, cnt; 1169 unsigned int cfg, cnt, ticks_per_int, lost_ints;
1168 1170
1169 if (unlikely(!(PIE_on | AIE_on | UIE_on))) { 1171 if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
1170 cfg = hpet_readl(HPET_T1_CFG); 1172 cfg = hpet_readl(HPET_T1_CFG);
@@ -1179,10 +1181,33 @@ static void hpet_rtc_timer_reinit(void)
1179 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; 1181 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
1180 1182
1181 /* It is more accurate to use the comparator value than current count.*/ 1183 /* It is more accurate to use the comparator value than current count.*/
1182 cnt = hpet_t1_cmp; 1184 ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
1183 cnt += hpet_tick*HZ/hpet_rtc_int_freq; 1185 hpet_t1_cmp += ticks_per_int;
1184 hpet_writel(cnt, HPET_T1_CMP); 1186 hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
1185 hpet_t1_cmp = cnt; 1187
1188 /*
1189 * If the interrupt handler was delayed too long, the write above tries
1190 * to schedule the next interrupt in the past and the hardware would
1191 * not interrupt until the counter had wrapped around.
1192 * So we have to check that the comparator wasn't set to a past time.
1193 */
1194 cnt = hpet_readl(HPET_COUNTER);
1195 if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
1196 lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
1197 /* Make sure that, even with the time needed to execute
1198 * this code, the next scheduled interrupt has been moved
1199 * back to the future: */
1200 lost_ints++;
1201
1202 hpet_t1_cmp += lost_ints * ticks_per_int;
1203 hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
1204
1205 if (PIE_on)
1206 PIE_count += lost_ints;
1207
1208 printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
1209 hpet_rtc_int_freq);
1210 }
1186} 1211}
1187 1212
1188/* 1213/*