]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86_64/kernel/time.c
[PATCH] x86_64: [PATCH] timer resume
[linux-2.6-omap-h63xx.git] / arch / x86_64 / kernel / time.c
index fdaddc4e52847a1f0fea0c50ff960aadcc9caf60..c1f76055b5780410a7cb2b7f1425aeda3377c42f 100644 (file)
@@ -51,7 +51,7 @@ extern int using_apic_timer;
 DEFINE_SPINLOCK(rtc_lock);
 DEFINE_SPINLOCK(i8253_lock);
 
-static int nohpet __initdata = 0;
+int nohpet __initdata = 0;
 static int notsc __initdata = 0;
 
 #undef HPET_HACK_ENABLE_DANGEROUS
@@ -59,7 +59,7 @@ static int notsc __initdata = 0;
 unsigned int cpu_khz;                                  /* TSC clocks / usec, not used here */
 static unsigned long hpet_period;                      /* fsecs / HPET clock */
 unsigned long hpet_tick;                               /* HPET clocks / interrupt */
-static int hpet_use_timer;
+static int hpet_use_timer;                             /* Use counter of hpet for time keeping, otherwise PIT */
 unsigned long vxtime_hz = PIT_TICK_RATE;
 int report_lost_ticks;                         /* command line option */
 unsigned long long monotonic_base;
@@ -71,14 +71,6 @@ unsigned long __wall_jiffies __section_wall_jiffies = INITIAL_JIFFIES;
 struct timespec __xtime __section_xtime;
 struct timezone __sys_tz __section_sys_tz;
 
-static inline void rdtscll_sync(unsigned long *tsc)
-{
-#ifdef CONFIG_SMP
-       sync_core();
-#endif
-       rdtscll(*tsc);
-}
-
 /*
  * do_gettimeoffset() returns microseconds since last timer interrupt was
  * triggered by hardware. A memory read of HPET is slower than a register read
@@ -93,7 +85,7 @@ static inline unsigned int do_gettimeoffset_tsc(void)
 {
        unsigned long t;
        unsigned long x;
-       rdtscll_sync(&t);
+       t = get_cycles_sync();
        if (t < vxtime.last_tsc) t = vxtime.last_tsc; /* hack */
        x = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> 32;
        return x;
@@ -259,8 +251,8 @@ static void set_rtc_mmss(unsigned long nowtime)
 #endif
 
        {
-                       BIN_TO_BCD(real_seconds);
-                       BIN_TO_BCD(real_minutes);
+               BIN_TO_BCD(real_seconds);
+               BIN_TO_BCD(real_minutes);
                CMOS_WRITE(real_seconds, RTC_SECONDS);
                CMOS_WRITE(real_minutes, RTC_MINUTES);
        }
@@ -297,25 +289,21 @@ unsigned long long monotonic_clock(void)
                        last_offset = vxtime.last;
                        base = monotonic_base;
                        this_offset = hpet_readl(HPET_COUNTER);
-
                } while (read_seqretry(&xtime_lock, seq));
                offset = (this_offset - last_offset);
                offset *=(NSEC_PER_SEC/HZ)/hpet_tick;
                return base + offset;
-       }else{
+       } else {
                do {
                        seq = read_seqbegin(&xtime_lock);
 
                        last_offset = vxtime.last_tsc;
                        base = monotonic_base;
                } while (read_seqretry(&xtime_lock, seq));
-               sync_core();
-               rdtscll(this_offset);
+               this_offset = get_cycles_sync();
                offset = (this_offset - last_offset)*1000/cpu_khz; 
                return base + offset;
        }
-
-
 }
 EXPORT_SYMBOL(monotonic_clock);
 
@@ -357,7 +345,7 @@ static noinline void handle_lost_ticks(int lost, struct pt_regs *regs)
 #endif
 }
 
-static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+void main_timer_handler(struct pt_regs *regs)
 {
        static unsigned long rtc_update = 0;
        unsigned long tsc;
@@ -391,7 +379,7 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                delay = LATCH - 1 - delay;
        }
 
-       rdtscll_sync(&tsc);
+       tsc = get_cycles_sync();
 
        if (vxtime.mode == VXTIME_HPET) {
                if (offset - vxtime.last > hpet_tick) {
@@ -470,7 +458,17 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        }
  
        write_sequnlock(&xtime_lock);
+}
 
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (apic_runs_main_timer > 1)
+               return IRQ_HANDLED;
+       main_timer_handler(regs);
+#ifdef CONFIG_X86_LOCAL_APIC
+       if (using_apic_timer)
+               smp_send_timer_broadcast_ipi();
+#endif
        return IRQ_HANDLED;
 }
 
@@ -509,10 +507,10 @@ unsigned long long sched_clock(void)
        return cycles_2_ns(a);
 }
 
-unsigned long get_cmos_time(void)
+static unsigned long get_cmos_time(void)
 {
-       unsigned int timeout, year, mon, day, hour, min, sec;
-       unsigned char last, this;
+       unsigned int timeout = 1000000, year, mon, day, hour, min, sec;
+       unsigned char uip = 0, this = 0;
        unsigned long flags;
 
 /*
@@ -525,45 +523,41 @@ unsigned long get_cmos_time(void)
 
        spin_lock_irqsave(&rtc_lock, flags);
 
-       timeout = 1000000;
-       last = this = 0;
-
-       while (timeout && last && !this) {
-               last = this;
+       while (timeout && (!uip || this)) {
+               uip |= this;
                this = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP;
                timeout--;
        }
 
-/*
- * Here we are safe to assume the registers won't change for a whole second, so
- * we just go ahead and read them.
-        */
-
-               sec = CMOS_READ(RTC_SECONDS);
-               min = CMOS_READ(RTC_MINUTES);
-               hour = CMOS_READ(RTC_HOURS);
-               day = CMOS_READ(RTC_DAY_OF_MONTH);
-               mon = CMOS_READ(RTC_MONTH);
-               year = CMOS_READ(RTC_YEAR);
+       /*
+        * Here we are safe to assume the registers won't change for a whole
+        * second, so we just go ahead and read them.
+        */
+       sec = CMOS_READ(RTC_SECONDS);
+       min = CMOS_READ(RTC_MINUTES);
+       hour = CMOS_READ(RTC_HOURS);
+       day = CMOS_READ(RTC_DAY_OF_MONTH);
+       mon = CMOS_READ(RTC_MONTH);
+       year = CMOS_READ(RTC_YEAR);
 
        spin_unlock_irqrestore(&rtc_lock, flags);
 
-/*
- * We know that x86-64 always uses BCD format, no need to check the config
* register.
- */
+       /*
+        * We know that x86-64 always uses BCD format, no need to check the
       * config register.
      */
 
-           BCD_TO_BIN(sec);
-           BCD_TO_BIN(min);
-           BCD_TO_BIN(hour);
-           BCD_TO_BIN(day);
-           BCD_TO_BIN(mon);
-           BCD_TO_BIN(year);
+       BCD_TO_BIN(sec);
+       BCD_TO_BIN(min);
+       BCD_TO_BIN(hour);
+       BCD_TO_BIN(day);
+       BCD_TO_BIN(mon);
+       BCD_TO_BIN(year);
 
-/*
- * x86-64 systems only exists since 2002.
- * This will work up to Dec 31, 2100
- */
+       /*
       * x86-64 systems only exists since 2002.
       * This will work up to Dec 31, 2100
       */
        year += 2000;
 
        return mktime(year, mon, day, hour, min, sec);
@@ -695,8 +689,7 @@ static unsigned int __init hpet_calibrate_tsc(void)
        do {
                local_irq_disable();
                hpet_now = hpet_readl(HPET_COUNTER);
-               sync_core();
-               rdtscl(tsc_now);
+               tsc_now = get_cycles_sync();
                local_irq_restore(flags);
        } while ((tsc_now - tsc_start) < TICK_COUNT &&
                 (hpet_now - hpet_start) < TICK_COUNT);
@@ -726,11 +719,9 @@ static unsigned int __init pit_calibrate_tsc(void)
        outb(0xb0, 0x43);
        outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
        outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
-       rdtscll(start);
-       sync_core();
+       start = get_cycles_sync();
        while ((inb(0x61) & 0x20) == 0);
-       sync_core();
-       rdtscll(end);
+       end = get_cycles_sync();
 
        spin_unlock_irqrestore(&i8253_lock, flags);
        
@@ -744,7 +735,7 @@ static __init int late_hpet_init(void)
        unsigned int            ntimer;
 
        if (!vxtime.hpet_address)
-          return -1;
+               return -1;
 
        memset(&hd, 0, sizeof (hd));
 
@@ -857,17 +848,43 @@ static int hpet_reenable(void)
        return hpet_timer_stop_set_go(hpet_tick);
 }
 
-void __init pit_init(void)
+#define PIT_MODE 0x43
+#define PIT_CH0  0x40
+
+static void __init __pit_init(int val, u8 mode)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&i8253_lock, flags);
-       outb_p(0x34, 0x43);             /* binary, mode 2, LSB/MSB, ch 0 */
-       outb_p(LATCH & 0xff, 0x40);     /* LSB */
-       outb_p(LATCH >> 8, 0x40);       /* MSB */
+       outb_p(mode, PIT_MODE);
+       outb_p(val & 0xff, PIT_CH0);    /* LSB */
+       outb_p(val >> 8, PIT_CH0);      /* MSB */
        spin_unlock_irqrestore(&i8253_lock, flags);
 }
 
+void __init pit_init(void)
+{
+       __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
+}
+
+void __init pit_stop_interrupt(void)
+{
+       __pit_init(0, 0x30); /* mode 0 */
+}
+
+void __init stop_timer_interrupt(void)
+{
+       char *name;
+       if (vxtime.hpet_address) {
+               name = "HPET";
+               hpet_timer_stop_set_go(0);
+       } else {
+               name = "PIT";
+               pit_stop_interrupt();
+       }
+       printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
+}
+
 int __init time_setup(char *str)
 {
        report_lost_ticks = 1;
@@ -878,8 +895,6 @@ static struct irqaction irq0 = {
        timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL
 };
 
-extern void __init config_acpi_tables(void);
-
 void __init time_init(void)
 {
        char *timename;
@@ -908,12 +923,14 @@ void __init time_init(void)
        if (!hpet_init())
                 vxtime_hz = (1000000000000000L + hpet_period / 2) /
                        hpet_period;
+       else
+               vxtime.hpet_address = 0;
 
        if (hpet_use_timer) {
                cpu_khz = hpet_calibrate_tsc();
                timename = "HPET";
 #ifdef CONFIG_X86_PM_TIMER
-       } else if (pmtmr_ioport) {
+       } else if (pmtmr_ioport && !vxtime.hpet_address) {
                vxtime_hz = PM_TIMER_FREQUENCY;
                timename = "PM";
                pit_init();
@@ -932,7 +949,7 @@ void __init time_init(void)
        vxtime.mode = VXTIME_TSC;
        vxtime.quot = (1000000L << 32) / vxtime_hz;
        vxtime.tsc_quot = (1000L << 32) / cpu_khz;
-       rdtscll_sync(&vxtime.last_tsc);
+       vxtime.last_tsc = get_cycles_sync();
        setup_irq(0, &irq0);
 
        set_cyc2ns_scale(cpu_khz);
@@ -946,7 +963,7 @@ void __init time_init(void)
  * Make an educated guess if the TSC is trustworthy and synchronized
  * over all CPUs.
  */
-static __init int unsynchronized_tsc(void)
+__init int unsynchronized_tsc(void)
 {
 #ifdef CONFIG_SMP
        if (oem_force_hpet_timer())
@@ -957,7 +974,7 @@ static __init int unsynchronized_tsc(void)
                return 0;
 #endif
        /* Assume multi socket systems are not synchronized */
-       return num_online_cpus() > 1;
+       return num_present_cpus() > 1;
 }
 
 /*
@@ -997,6 +1014,10 @@ __setup("report_lost_ticks", time_setup);
 static long clock_cmos_diff;
 static unsigned long sleep_start;
 
+/*
+ * sysfs support for the timer.
+ */
+
 static int timer_suspend(struct sys_device *dev, pm_message_t state)
 {
        /*
@@ -1026,9 +1047,21 @@ static int timer_resume(struct sys_device *dev)
        write_seqlock_irqsave(&xtime_lock,flags);
        xtime.tv_sec = sec;
        xtime.tv_nsec = 0;
+       if (vxtime.mode == VXTIME_HPET) {
+               if (hpet_use_timer)
+                       vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
+               else
+                       vxtime.last = hpet_readl(HPET_COUNTER);
+#ifdef CONFIG_X86_PM_TIMER
+       } else if (vxtime.mode == VXTIME_PMTMR) {
+               pmtimer_resume();
+#endif
+       } else
+               vxtime.last_tsc = get_cycles_sync();
        write_sequnlock_irqrestore(&xtime_lock,flags);
        jiffies += sleep_length;
        wall_jiffies += sleep_length;
+       monotonic_base += sleep_length * (NSEC_PER_SEC/HZ);
        touch_softlockup_watchdog();
        return 0;
 }
@@ -1039,7 +1072,6 @@ static struct sysdev_class timer_sysclass = {
        set_kset_name("timer"),
 };
 
-
 /* XXX this driverfs stuff should probably go elsewhere later -john */
 static struct sys_device device_timer = {
        .id     = 0,
@@ -1073,8 +1105,6 @@ device_initcall(time_init_device);
  */
 #include <linux/rtc.h>
 
-extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-
 #define DEFAULT_RTC_INT_FREQ   64
 #define RTC_NUM_INTS           1
 
@@ -1283,8 +1313,6 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 }
 #endif
 
-
-
 static int __init nohpet_setup(char *s) 
 { 
        nohpet = 1;
@@ -1301,5 +1329,3 @@ static int __init notsc_setup(char *s)
 }
 
 __setup("notsc", notsc_setup);
-
-