aboutsummaryrefslogtreecommitdiffstats
path: root/arch
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
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')
-rw-r--r--arch/i386/kernel/time_hpet.c37
-rw-r--r--arch/x86_64/kernel/time.c37
2 files changed, 62 insertions, 12 deletions
diff --git a/arch/i386/kernel/time_hpet.c b/arch/i386/kernel/time_hpet.c
index 14a1376fedd1..6bf14a4e995e 100644
--- a/arch/i386/kernel/time_hpet.c
+++ b/arch/i386/kernel/time_hpet.c
@@ -301,23 +301,25 @@ int hpet_rtc_timer_init(void)
301 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; 301 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
302 302
303 local_irq_save(flags); 303 local_irq_save(flags);
304
304 cnt = hpet_readl(HPET_COUNTER); 305 cnt = hpet_readl(HPET_COUNTER);
305 cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq); 306 cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
306 hpet_writel(cnt, HPET_T1_CMP); 307 hpet_writel(cnt, HPET_T1_CMP);
307 hpet_t1_cmp = cnt; 308 hpet_t1_cmp = cnt;
308 local_irq_restore(flags);
309 309
310 cfg = hpet_readl(HPET_T1_CFG); 310 cfg = hpet_readl(HPET_T1_CFG);
311 cfg &= ~HPET_TN_PERIODIC; 311 cfg &= ~HPET_TN_PERIODIC;
312 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; 312 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
313 hpet_writel(cfg, HPET_T1_CFG); 313 hpet_writel(cfg, HPET_T1_CFG);
314 314
315 local_irq_restore(flags);
316
315 return 1; 317 return 1;
316} 318}
317 319
318static void hpet_rtc_timer_reinit(void) 320static void hpet_rtc_timer_reinit(void)
319{ 321{
320 unsigned int cfg, cnt; 322 unsigned int cfg, cnt, ticks_per_int, lost_ints;
321 323
322 if (unlikely(!(PIE_on | AIE_on | UIE_on))) { 324 if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
323 cfg = hpet_readl(HPET_T1_CFG); 325 cfg = hpet_readl(HPET_T1_CFG);
@@ -332,10 +334,33 @@ static void hpet_rtc_timer_reinit(void)
332 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; 334 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
333 335
334 /* It is more accurate to use the comparator value than current count.*/ 336 /* It is more accurate to use the comparator value than current count.*/
335 cnt = hpet_t1_cmp; 337 ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
336 cnt += hpet_tick*HZ/hpet_rtc_int_freq; 338 hpet_t1_cmp += ticks_per_int;
337 hpet_writel(cnt, HPET_T1_CMP); 339 hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
338 hpet_t1_cmp = cnt; 340
341 /*
342 * If the interrupt handler was delayed too long, the write above tries
343 * to schedule the next interrupt in the past and the hardware would
344 * not interrupt until the counter had wrapped around.
345 * So we have to check that the comparator wasn't set to a past time.
346 */
347 cnt = hpet_readl(HPET_COUNTER);
348 if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
349 lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
350 /* Make sure that, even with the time needed to execute
351 * this code, the next scheduled interrupt has been moved
352 * back to the future: */
353 lost_ints++;
354
355 hpet_t1_cmp += lost_ints * ticks_per_int;
356 hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
357
358 if (PIE_on)
359 PIE_count += lost_ints;
360
361 printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
362 hpet_rtc_int_freq);
363 }
339} 364}
340 365
341/* 366/*
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/*