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;
 

Reply via email to