The HyperNews Linux KHG Discussion Pages

## Re: fixed, patch for kernel 2.0.30

  Forum: The Linux Kernel Hackers' Guide
  Re: reset the irq 0 timer after APM suspend (Dong Chen)
  Keywords: timer, APM
  Date: Fri, 27 Jun 1997 15:44:46 GMT
  From: Dong Chen <chen@ctp.mit.edu>

    (This is the message I sent to the linux-kernel mailing list)

    Hi,

    This is a patch for "drivers/char/apm_bios.c", it fixes the following
    problems:

    (1) On some notebooks (AST J series, for example), the timer on interrupt 0
    is reset to DOS default: 18 Hz.  This patch re-initialize it to 100 Hz.
    Thanks to Pavel (pavel@Elf.mj.gts.cz) for pointing out to me that I should
    add some delays after the outb_p() and outb() calls.

    (2) The clock is not correctly restored after a standby().

    There are still some problems with not getting the correct time after APM
    suspend or standby, namely before the first suspend() or standby()
    call, if the clock is already slowed by CPU_IDLE call, then the estimate
    time zone "clock_cmos_diff" would be wrong.  Ideally, "clock_cmos_diff"
    should be setup at boot time after the time zone is set.  But that
    will require changing code other than "apm_bios.c".  Also, APM will not
    correct for the change between daylight savings time and normal time.

    Dong Chen
    chen@ctp.mit.edu

    ---------------------------CUT HERE-------------------------------------
    --- drivers/char/apm_bios.c.orig        Mon May 26 11:05:15 1997
    +++ drivers/char/apm_bios.c     Tue Jun 24 12:09:06 1997
    @@ -73,6 +73,18 @@
     #include <linux/miscdevice.h>
     #include <linux/apm_bios.h>

    +/*
    + * INIT_TIMER_AFTER_SUSPEND: define to re-initialize the interrupt 0 timer
    + * to 100 Hz after a suspend.
    + */
    +#define INIT_TIMER_AFTER_SUSPEND
    +
    +#ifdef INIT_TIMER_AFTER_SUSPEND
    +#include <linux/timex.h>
    +#include <asm/io.h>
    +#include <linux/delay.h>
    +#endif
    + static struct symbol_table     apm_syms = {
     #include <linux/symtab_begin.h>
            X(apm_register_callback),
    @@ -627,28 +639,53 @@
            unsigned long   flags;
            int             err;

    -                               /* Estimate time zone so that set_time can
    -                                   update the clock */
    -       save_flags(flags);
    -       clock_cmos_diff = -get_cmos_time();
    -       cli();
    -       clock_cmos_diff += CURRENT_TIME;
    -       got_clock_diff = 1;
    -       restore_flags(flags);
    +       if (!got_clock_diff) {
    +                               /* Estimate time zone */
    +               save_flags(flags);
    +               clock_cmos_diff = -get_cmos_time();
    +               cli();
    +               clock_cmos_diff += CURRENT_TIME;
    +               got_clock_diff = 1;
    +               restore_flags(flags);
    +       }

            err = apm_set_power_state(APM_STATE_SUSPEND);
            if (err)
                    apm_error("suspend", err);
    +
    +#ifdef INIT_TIMER_AFTER_SUSPEND
    +       cli();
    +        /* set the clock to 100 Hz */
    +        outb_p(0x34,0x43);              /* binary, mode 2, LSB/MSB, ch 0 */
    +        udelay(10);
    +        outb_p(LATCH & 0xff , 0x40);    /* LSB */
    +        udelay(10);
    +        outb(LATCH >> 8 , 0x40);        /* MSB */
    +        udelay(10);
    +#endif
    +
            set_time();
     }

     static void standby(void)
     {
    +       unsigned long   flags;
            int     err;

    +       if (!got_clock_diff) {
    +                               /* Estimate time zone */
    +               save_flags(flags);
    +               clock_cmos_diff = -get_cmos_time();
    +               cli();
    +               clock_cmos_diff += CURRENT_TIME;
    +               got_clock_diff = 1;
    +               restore_flags(flags);
    +       }
    +
            err = apm_set_power_state(APM_STATE_STANDBY);
            if (err)
                    apm_error("standby", err);
    +       set_time();
     }

     static apm_event_t get_event(void)