This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / m32r / kernel / time.c
diff --git a/arch/m32r/kernel/time.c b/arch/m32r/kernel/time.c
new file mode 100644 (file)
index 0000000..25a3166
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ *  linux/arch/m32r/kernel/time.c
+ *
+ *  Copyright (c) 2001, 2002  Hiroyuki Kondo, Hirokazu Takata,
+ *                            Hitoshi Yamamoto
+ *  Taken from i386 version.
+ *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
+ *    Copyright (C) 1996, 1997, 1998  Ralf Baechle
+ *
+ *  This file contains the time handling details for PC-style clocks as
+ *  found in some MIPS systems.
+ *
+ *  Some code taken from sh version.
+ *    Copyright (C) 1999  Tetsuya Okada & Niibe Yutaka
+ *    Copyright (C) 2000  Philipp Rumpf <prumpf@tux.org>
+ */
+
+#undef  DEBUG_TIMER
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/profile.h>
+
+#include <asm/io.h>
+#include <asm/m32r.h>
+
+#include <asm/hw_irq.h>
+
+#ifdef CONFIG_SMP
+extern void send_IPI_allbutself(int, int);
+extern void smp_local_timer_interrupt(struct pt_regs *);
+#endif
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+
+EXPORT_SYMBOL(jiffies_64);
+
+extern unsigned long wall_jiffies;
+#define TICK_SIZE      (tick_nsec / 1000)
+
+/*
+ * Change this if you have some constant time drift
+ */
+
+/* This is for machines which generate the exact clock. */
+#define USECS_PER_JIFFY (1000000/HZ)
+
+static unsigned long latch;
+
+static unsigned long do_gettimeoffset(void)
+{
+       unsigned long  elapsed_time = 0;  /* [us] */
+
+#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \
+       || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \
+       || defined(CONFIG_CHIP_OPSP)
+#ifndef CONFIG_SMP
+
+       unsigned long count;
+
+       /* timer count may underflow right here */
+       count = inl(M32R_MFT2CUT_PORTL);
+
+       if (inl(M32R_ICU_CR18_PORTL) & 0x00000100)      /* underflow check */
+               count = 0;
+
+       count = (latch - count) * TICK_SIZE;
+       elapsed_time = (count + latch / 2) / latch;
+       /* NOTE: LATCH is equal to the "interval" value (= reload count). */
+
+#else /* CONFIG_SMP */
+       unsigned long count;
+       static unsigned long p_jiffies = -1;
+       static unsigned long p_count = 0;
+
+       /* timer count may underflow right here */
+       count = inl(M32R_MFT2CUT_PORTL);
+
+       if (jiffies == p_jiffies && count > p_count)
+               count = 0;
+
+       p_jiffies = jiffies;
+       p_count = count;
+
+       count = (latch - count) * TICK_SIZE;
+       elapsed_time = (count + latch / 2) / latch;
+       /* NOTE: LATCH is equal to the "interval" value (= reload count). */
+#endif /* CONFIG_SMP */
+#elif defined(CONFIG_CHIP_M32310)
+#warning do_gettimeoffse not implemented
+#else
+#error no chip configuration
+#endif
+
+       return elapsed_time;
+}
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+       unsigned long seq;
+       unsigned long usec, sec;
+       unsigned long max_ntp_tick = tick_usec - tickadj;
+
+       do {
+               unsigned long lost;
+
+               seq = read_seqbegin(&xtime_lock);
+
+               usec = do_gettimeoffset();
+               lost = jiffies - wall_jiffies;
+
+               /*
+                * If time_adjust is negative then NTP is slowing the clock
+                * so make sure not to go into next possible interval.
+                * Better to lose some accuracy than have time go backwards..
+                */
+               if (unlikely(time_adjust < 0)) {
+                       usec = min(usec, max_ntp_tick);
+                       if (lost)
+                               usec += lost * max_ntp_tick;
+               } else if (unlikely(lost))
+                       usec += lost * tick_usec;
+
+               sec = xtime.tv_sec;
+               usec += (xtime.tv_nsec / 1000);
+       } while (read_seqretry(&xtime_lock, seq));
+
+       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 "xtime" correctly. However, the
+        * value in this location is the value at the most recent update of
+        * wall time.  Discover what correction gettimeofday() would have
+        * made, and then undo it!
+        */
+       nsec -= do_gettimeoffset() * NSEC_PER_USEC;
+       nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
+
+       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);
+
+       time_adjust = 0;                /* stop active adjtime() */
+       time_status |= STA_UNSYNC;
+       time_maxerror = NTP_PHASE_LIMIT;
+       time_esterror = NTP_PHASE_LIMIT;
+       write_sequnlock_irq(&xtime_lock);
+       clock_was_set();
+
+       return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be
+ * called 500 ms after the second nowtime has started, because when
+ * nowtime is written into the registers of the CMOS clock, it will
+ * jump to the next second precisely 500 ms later. Check the Motorola
+ * MC146818A or Dallas DS12887 data sheet for details.
+ *
+ * BUG: This routine does not handle hour overflow properly; it just
+ *      sets the minutes. Usually you won't notice until after reboot!
+ */
+static __inline__ int set_rtc_mmss(unsigned long nowtime)
+{
+       return 0;
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static __inline__ void do_timer_interrupt(int irq, void *dev_id,
+       struct pt_regs * regs)
+{
+       do_timer(regs);
+
+       /*
+        * If we have an externally synchronized Linux clock, then update
+        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+        * called as close as possible to 500 ms before the new second starts.
+        */
+       if ((time_status & STA_UNSYNC) == 0
+               && xtime.tv_sec > last_rtc_update + 660
+               && (xtime.tv_nsec / 1000) >= 500000 - ((unsigned)TICK_SIZE) / 2
+               && (xtime.tv_nsec / 1000) <= 500000 + ((unsigned)TICK_SIZE) / 2)
+       {
+               if (set_rtc_mmss(xtime.tv_sec) == 0)
+                       last_rtc_update = xtime.tv_sec;
+               else    /* do it again in 60 s */
+                       last_rtc_update = xtime.tv_sec - 600;
+       }
+       /* As we return to user mode fire off the other CPU schedulers..
+          this is basically because we don't yet share IRQ's around.
+          This message is rigged to be safe on the 386 - basically it's
+          a hack, so don't look closely for now.. */
+
+#ifdef CONFIG_SMP
+       smp_local_timer_interrupt(regs);
+#endif
+}
+
+irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       write_seqlock(&xtime_lock);
+       do_timer_interrupt(irq, NULL, regs);
+       write_sequnlock(&xtime_lock);
+
+#ifndef CONFIG_SMP
+       profile_tick(CPU_PROFILING, regs);
+#endif
+
+       return IRQ_HANDLED;
+}
+
+struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE,
+                         "MFT2", NULL, NULL };
+
+void __init time_init(void)
+{
+       unsigned int epoch, year, mon, day, hour, min, sec;
+
+       sec = min = hour = day = mon = year = 0;
+       epoch = 0;
+
+       year = 23;
+       mon = 4;
+       day = 17;
+
+       /* Attempt to guess the epoch.  This is the same heuristic as in rtc.c
+          so no stupid things will happen to timekeeping.  Who knows, maybe
+          Ultrix also uses 1952 as epoch ...  */
+       if (year > 10 && year < 44)
+               epoch = 1980;
+       else if (year < 96)
+               epoch = 1952;
+       year += epoch;
+
+       xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+       xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
+       wall_to_monotonic.tv_sec = -xtime.tv_sec;
+       wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
+
+#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \
+       || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \
+       || defined(CONFIG_CHIP_OPSP)
+
+       /* M32102 MFT setup */
+       setup_irq(M32R_IRQ_MFT2, &irq0);
+       {
+               unsigned long bus_clock;
+               unsigned short divide;
+
+               bus_clock = boot_cpu_data.bus_clock;
+               divide = boot_cpu_data.timer_divide;
+               latch = (bus_clock/divide + HZ / 2) / HZ;
+
+               printk("Timer start : latch = %ld\n", latch);
+
+               outl((M32R_MFTMOD_CC_MASK | M32R_MFTMOD_TCCR \
+                       |M32R_MFTMOD_CSSEL011), M32R_MFT2MOD_PORTL);
+               outl(latch, M32R_MFT2RLD_PORTL);
+               outl(latch, M32R_MFT2CUT_PORTL);
+               outl(0, M32R_MFT2CMPRLD_PORTL);
+               outl((M32R_MFTCR_MFT2MSK|M32R_MFTCR_MFT2EN), M32R_MFTCR_PORTL);
+       }
+
+#elif defined(CONFIG_CHIP_M32310)
+#warning time_init not implemented
+#else
+#error no chip configuration
+#endif
+}
+
+/*
+ *  Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+       return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+