fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / s390 / kernel / time.c
index fea043b..6cceed4 100644 (file)
@@ -12,7 +12,6 @@
  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/profile.h>
 #include <linux/timex.h>
 #include <linux/notifier.h>
+#include <linux/clocksource.h>
 
 #include <asm/uaccess.h>
 #include <asm/delay.h>
 #include <asm/s390_ext.h>
 #include <asm/div64.h>
 #include <asm/irq.h>
+#include <asm/irq_regs.h>
 #include <asm/timer.h>
 
 /* change this if you have some constant time drift */
@@ -54,8 +55,6 @@ static u64 init_timer_cc;
 static u64 jiffies_timer_cc;
 static u64 xtime_cc;
 
-extern unsigned long wall_jiffies;
-
 /*
  * Scheduler clock - returns current time in nanosec units.
  */
@@ -84,79 +83,10 @@ void tod_to_timeval(__u64 todval, struct timespec *xtime)
        xtime->tv_nsec = ((todval * 1000) >> 12);
 }
 
-static inline unsigned long do_gettimeoffset(void) 
-{
-       __u64 now;
-
-        now = (get_clock() - jiffies_timer_cc) >> 12;
-       /* We require the offset from the latest update of xtime */
-       now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
-       return (unsigned long) now;
-}
-
-/*
- * This version of gettimeofday has microsecond resolution.
- */
-void do_gettimeofday(struct timeval *tv)
-{
-       unsigned long flags;
-       unsigned long seq;
-       unsigned long usec, sec;
-
-       do {
-               seq = read_seqbegin_irqsave(&xtime_lock, flags);
-
-               sec = xtime.tv_sec;
-               usec = xtime.tv_nsec / 1000 + do_gettimeoffset();
-       } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
-
-       while (usec >= 1000000) {
-               usec -= 1000000;
-               sec++;
-       }
-
-       tv->tv_sec = sec;
-       tv->tv_usec = usec;
-}
-
-EXPORT_SYMBOL(do_gettimeofday);
-
-int do_settimeofday(struct timespec *tv)
-{
-       time_t wtm_sec, sec = tv->tv_sec;
-       long wtm_nsec, nsec = tv->tv_nsec;
-
-       if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
-               return -EINVAL;
-
-       write_seqlock_irq(&xtime_lock);
-       /* This is revolting. We need to set the xtime.tv_nsec
-        * correctly. However, the value in this location is
-        * is value at the last tick.
-        * Discover what correction gettimeofday
-        * would have done, and then undo it!
-        */
-       nsec -= do_gettimeoffset() * 1000;
-
-       wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
-       wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
-
-       set_normalized_timespec(&xtime, sec, nsec);
-       set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
-
-       ntp_clear();
-       write_sequnlock_irq(&xtime_lock);
-       clock_was_set();
-       return 0;
-}
-
-EXPORT_SYMBOL(do_settimeofday);
-
-
 #ifdef CONFIG_PROFILING
-#define s390_do_profile(regs)  profile_tick(CPU_PROFILING, regs)
+#define s390_do_profile()      profile_tick(CPU_PROFILING)
 #else
-#define s390_do_profile(regs)  do { ; } while(0)
+#define s390_do_profile()      do { ; } while(0)
 #endif /* CONFIG_PROFILING */
 
 
@@ -164,10 +94,10 @@ EXPORT_SYMBOL(do_settimeofday);
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-void account_ticks(struct pt_regs *regs)
+void account_ticks(void)
 {
        __u64 tmp;
-       __u32 ticks, xticks;
+       __u32 ticks;
 
        /* Calculate how many ticks have passed. */
        if (S390_lowcore.int_clock < S390_lowcore.jiffy_timer) {
@@ -205,6 +135,7 @@ void account_ticks(struct pt_regs *regs)
         */
        write_seqlock(&xtime_lock);
        if (S390_lowcore.jiffy_timer > xtime_cc) {
+               __u32 xticks;
                tmp = S390_lowcore.jiffy_timer - xtime_cc;
                if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
                        xticks = __div(tmp, CLK_TICKS_PER_JIFFY);
@@ -213,23 +144,21 @@ void account_ticks(struct pt_regs *regs)
                        xticks = 1;
                        xtime_cc += CLK_TICKS_PER_JIFFY;
                }
-               while (xticks--)
-                       do_timer(regs);
+               do_timer(xticks);
        }
        write_sequnlock(&xtime_lock);
 #else
-       for (xticks = ticks; xticks > 0; xticks--)
-               do_timer(regs);
+       do_timer(ticks);
 #endif
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING
        account_tick_vtime(current);
 #else
        while (ticks--)
-               update_process_times(user_mode(regs));
+               update_process_times(user_mode(get_irq_regs()));
 #endif
 
-       s390_do_profile(regs);
+       s390_do_profile();
 }
 
 #ifdef CONFIG_NO_IDLE_HZ
@@ -249,18 +178,19 @@ static inline void stop_hz_timer(void)
        unsigned long flags;
        unsigned long seq, next;
        __u64 timer, todval;
+       int cpu = smp_processor_id();
 
        if (sysctl_hz_timer != 0)
                return;
 
-       cpu_set(smp_processor_id(), nohz_cpu_mask);
+       cpu_set(cpu, nohz_cpu_mask);
 
        /*
         * Leave the clock comparator set up for the next timer
         * tick if either rcu or a softirq is pending.
         */
-       if (rcu_pending(smp_processor_id()) || local_softirq_pending()) {
-               cpu_clear(smp_processor_id(), nohz_cpu_mask);
+       if (rcu_needs_cpu(cpu) || local_softirq_pending()) {
+               cpu_clear(cpu, nohz_cpu_mask);
                return;
        }
 
@@ -271,7 +201,7 @@ static inline void stop_hz_timer(void)
        next = next_timer_interrupt();
        do {
                seq = read_seqbegin_irqsave(&xtime_lock, flags);
-               timer = (__u64)(next - jiffies) + jiffies_64;
+               timer = ((__u64) next) - ((__u64) jiffies) + jiffies_64;
        } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
        todval = -1ULL;
        /* Be careful about overflows. */
@@ -289,9 +219,11 @@ static inline void stop_hz_timer(void)
  */
 static inline void start_hz_timer(void)
 {
+       BUG_ON(!in_interrupt());
+
        if (!cpu_isset(smp_processor_id(), nohz_cpu_mask))
                return;
-       account_ticks(task_pt_regs(current));
+       account_ticks();
        cpu_clear(smp_processor_id(), nohz_cpu_mask);
 }
 
@@ -341,6 +273,22 @@ void init_cpu_timer(void)
 
 extern void vtime_init(void);
 
+static cycle_t read_tod_clock(void)
+{
+       return get_clock();
+}
+
+static struct clocksource clocksource_tod = {
+       .name           = "tod",
+       .rating         = 100,
+       .read           = read_tod_clock,
+       .mask           = -1ULL,
+       .mult           = 1000,
+       .shift          = 12,
+       .is_continuous  = 1,
+};
+
+
 /*
  * Initialize the TOD clock and the CPU timer of
  * the boot cpu.
@@ -351,10 +299,12 @@ void __init time_init(void)
        int cc;
 
         /* kick the TOD clock */
-        asm volatile ("STCK 0(%1)\n\t"
-                      "IPM  %0\n\t"
-                      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
-                                  : "memory", "cc");
+       asm volatile(
+               "       stck    0(%2)\n"
+               "       ipm     %0\n"
+               "       srl     %0,28"
+               : "=d" (cc), "=m" (init_timer_cc)
+               : "a" (&init_timer_cc) : "cc");
         switch (cc) {
         case 0: /* clock in set state: all is fine */
                 break;
@@ -379,10 +329,13 @@ void __init time_init(void)
                                 -xtime.tv_sec, -xtime.tv_nsec);
 
        /* request the clock comparator external interrupt */
-        if (register_early_external_interrupt(0x1004, 0,
+       if (register_early_external_interrupt(0x1004, NULL,
                                              &ext_int_info_cc) != 0)
                 panic("Couldn't request external interrupt 0x1004");
 
+       if (clocksource_register(&clocksource_tod) != 0)
+               panic("Could not register TOD clock source");
+
         init_cpu_timer();
 
 #ifdef CONFIG_NO_IDLE_HZ