aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m32r/kernel/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m32r/kernel/time.c')
-rw-r--r--arch/m32r/kernel/time.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/arch/m32r/kernel/time.c b/arch/m32r/kernel/time.c
new file mode 100644
index 000000000000..3c4707280a52
--- /dev/null
+++ b/arch/m32r/kernel/time.c
@@ -0,0 +1,318 @@
+/*
+ * 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)
+{
+#ifndef CONFIG_SMP
+ profile_tick(CPU_PROFILING, regs);
+#endif
+ do_timer(regs);
+
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(regs));
+#endif
+ /*
+ * 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);
+
+ 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);
+ set_normalized_timespec(&wall_to_monotonic,
+ -xtime.tv_sec, -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);
+}