Let's begin exposing the background: Some laptops suffer of the "statclock broken" problem: APM suspend does not work while the RTC is generating periodic interrupts (which are used for the statistical clock). Currently, the "solution" consists of setting the 0x20 flag for the apm(4) driver. If this flag is set, the cpu_initclocks() routine in sys/i386/isa/clock.c does not initialize the statclok at all. As a consequence, the functionality of the statclock is lost. I tried another solution which seems to work for my laptop (a Dell I3.7k). I added two small routines to clock.c, named statclock_stop() and statclock_restart(). The first one simply disables the periodic interrupts from the MC146818A RTC; the second one re-enables those interrupts. Then, I modified apm_default_suspend() and apm_default_resume() (in sys/i386/apm/apm.c) for respectively stopping and restarting the statclock if the 0x20 flag is set. With these small modifications, now I can suspend and resume my laptop while retaining the statclock functionality. I attach a patchfile (against 4.0-RELEASE) to this message. It's possible that my solution needs to be reworked in order to make it SMP-safe or something. I would love to hear any comments. Saludos, -- JMA ----------------------------------------------------------------------- José Mª Alcaide | mailto:[EMAIL PROTECTED] Universidad del País Vasco | mailto:[EMAIL PROTECTED] Dpto. de Electricidad y Electrónica | http://www.we.lc.ehu.es/~jose Facultad de Ciencias - Campus de Lejona | Tel.: +34-946012479 48940 Lejona (Vizcaya) - SPAIN | Fax: +34-946013071 ----------------------------------------------------------------------- "Beware of Programmers who carry screwdrivers" -- Leonard Brandwein
--- sys/i386/isa/clock.c.orig Tue Jan 4 23:24:59 2000 +++ sys/i386/isa/clock.c Thu Apr 6 17:05:26 2000 @@ -132,7 +132,6 @@ int clkintr_pending; int disable_rtc_set; /* disable resettodr() if != 0 */ volatile u_int idelayed; -int statclock_disable; u_int stat_imask = SWI_CLOCK_MASK; #ifndef TIMER_FREQ #define TIMER_FREQ 1193182 @@ -827,6 +826,28 @@ #endif /* !defined(SMP) */ } +/* The following two functions are used in apm.c for stopping and + * restarting the statclock interrupts from the RTC, if the apm's + * broken_statclock flag is active (some laptops don't enter suspend + * mode while the RTC is generating interrupts) */ + +void +statclock_stop(void) +{ + /* disable RTC interrupts and clear any pending one */ + writertc(RTC_STATUSB, RTCSB_24HR); + rtcin(RTC_INTR); +} + +void +statclock_restart(void) +{ + /* don't trust the APM BIOS (paranoia?) */ + rtcin(RTC_INTR); + /* re-enable periodic interrupts */ + writertc(RTC_STATUSB, rtc_statusb); +} + /* * Initialize the time of day register, based on the time base which is, e.g. * from a filesystem. @@ -975,20 +996,9 @@ struct intrec *clkdesc; #endif /* APIC_IO */ - if (statclock_disable) { - /* - * The stat interrupt mask is different without the - * statistics clock. Also, don't set the interrupt - * flag which would normally cause the RTC to generate - * interrupts. - */ - stat_imask = HWI_MASK | SWI_MASK; - rtc_statusb = RTCSB_24HR; - } else { - /* Setting stathz to nonzero early helps avoid races. */ - stathz = RTC_NOPROFRATE; - profhz = RTC_PROFRATE; - } + /* Setting stathz to nonzero early helps avoid races. */ + stathz = RTC_NOPROFRATE; + profhz = RTC_PROFRATE; /* Finish initializing 8253 timer 0. */ #ifdef APIC_IO @@ -1023,9 +1033,6 @@ writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, RTCSB_24HR); - /* Don't bother enabling the statistics clock. */ - if (statclock_disable) - return; diag = rtcin(RTC_DIAG); if (diag != 0) printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS); --- sys/i386/include/clock.h.orig Wed Dec 29 05:32:58 1999 +++ sys/i386/include/clock.h Thu Apr 6 17:00:42 2000 @@ -16,7 +16,6 @@ */ extern int adjkerntz; extern int disable_rtc_set; -extern int statclock_disable; extern u_int timer_freq; extern int timer0_max_count; extern u_int tsc_freq; @@ -45,6 +44,8 @@ #endif int sysbeep __P((int pitch, int period)); void i8254_restore __P((void)); +void statclock_stop __P((void)); +void statclock_restart __P((void)); #endif /* _KERNEL */ --- sys/i386/apm/apm.c.orig Sun Feb 6 15:57:05 2000 +++ sys/i386/apm/apm.c Thu Apr 6 17:01:58 2000 @@ -56,6 +56,8 @@ static u_long apm_version; +static int broken_statclock = 0; + int apm_evindex; #define SCFLAG_ONORMAL 0x0000001 @@ -402,6 +404,10 @@ u_int second, minute, hour; struct timeval resume_time, tmp_time; + /* re-enable statclock if it's broken */ + if (broken_statclock) + statclock_restart(); + /* modified for adjkerntz */ pl = splsoftclock(); i8254_restore(); /* restore timer_freq and hz */ @@ -451,6 +457,11 @@ microtime(&suspend_time); timevalsub(&diff_time, &suspend_time); splx(pl); + + /* stop statclock if it's broken */ + if (broken_statclock) + statclock_stop(); + return 0; } @@ -1003,7 +1014,7 @@ flags = 0; if (flags & 0x20) - statclock_disable = 1; + broken_statclock = 1; sc->initialized = 0;