patch-2.1.51 linux/arch/ppc/kernel/time.c
Next file: linux/arch/ppc/kernel/time.h
Previous file: linux/arch/ppc/kernel/syscalls.c
Back to the patch index
Back to the overall index
- Lines: 222
- Date:
Sat Aug 16 09:51:08 1997
- Orig file:
v2.1.50/linux/arch/ppc/kernel/time.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.50/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
@@ -0,0 +1,221 @@
+/*
+ * $Id: time.c,v 1.8 1997/08/11 08:37:51 cort Exp $
+ * Common time routines among all ppc machines.
+ *
+ * Written by Cort Dougan (cort@cs.nmt.edu) to merge
+ * Paul Mackerras' version and mine for PReP and Pmac.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.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/timex.h>
+#include <linux/kernel_stat.h>
+#include <linux/mc146818rtc.h>
+#include <linux/time.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/nvram.h>
+
+#include "time.h"
+
+/* this is set to the appropriate pmac/prep func in init_IRQ() */
+int (*set_rtc_time)(unsigned long);
+
+/* keep track of when we need to update the rtc */
+unsigned long last_rtc_update = 0;
+
+/* The decrementer counts down by 128 every 128ns on a 601. */
+#define DECREMENTER_COUNT_601 (1000000000 / HZ)
+#define COUNT_PERIOD_NUM_601 1
+#define COUNT_PERIOD_DEN_601 1000
+
+unsigned decrementer_count; /* count value for 1e6/HZ microseconds */
+unsigned count_period_num; /* 1 decrementer count equals */
+unsigned count_period_den; /* count_period_num / count_period_den us */
+
+/* Accessor functions for the decrementer register. */
+inline unsigned long
+get_dec(void)
+{
+ int ret;
+
+ asm volatile("mfspr %0,22" : "=r" (ret) :);
+ return ret;
+}
+
+inline void
+set_dec(int val)
+{
+ asm volatile("mtspr 22,%0" : : "r" (val));
+}
+
+/*
+ * timer_interrupt - gets called when the decrementer overflows,
+ * with interrupts disabled.
+ * We set it up to overflow again in 1/HZ seconds.
+ */
+void timer_interrupt(struct pt_regs * regs)
+{
+ int dval, d;
+ while ((dval = get_dec()) < 0) {
+ /*
+ * Wait for the decrementer to change, then jump
+ * in and add decrementer_count to its value
+ * (quickly, before it changes again!)
+ */
+ while ((d = get_dec()) == dval)
+ ;
+ set_dec(d + decrementer_count);
+ do_timer(regs);
+ /*
+ * update the rtc when needed
+ */
+ if ( xtime.tv_sec > last_rtc_update + 660 )
+ if (set_rtc_time(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
+}
+
+/*
+ * This version of gettimeofday has microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ *tv = xtime;
+ tv->tv_usec += (decrementer_count - get_dec())
+ * count_period_num / count_period_den;
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+ restore_flags(flags);
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ int frac_tick;
+
+ last_rtc_update = 0; /* so the rtc gets updated soon */
+
+ frac_tick = tv->tv_usec % (1000000 / HZ);
+ save_flags(flags);
+ cli();
+ xtime.tv_sec = tv->tv_sec;
+ xtime.tv_usec = tv->tv_usec - frac_tick;
+ set_dec(frac_tick * count_period_den / count_period_num);
+ restore_flags(flags);
+}
+
+
+void
+time_init(void)
+{
+#ifdef CONFIG_PREP
+ /* pmac hasn't yet called via_cuda_init() */
+ if ( _machine != _MACH_Pmac ) /* prep */
+ {
+ xtime.tv_sec = prep_get_rtc_time();
+ xtime.tv_usec = 0;
+ /*
+ * mark the rtc/on-chip timer as in sync
+ * so we don't update right away
+ */
+ last_rtc_update = xtime.tv_sec;
+ }
+#endif /* CONFIG_PREP */
+ if ((_get_PVR() >> 16) == 1) {
+ /* 601 processor: dec counts down by 128 every 128ns */
+ decrementer_count = DECREMENTER_COUNT_601;
+ count_period_num = COUNT_PERIOD_NUM_601;
+ count_period_den = COUNT_PERIOD_DEN_601;
+ } else {
+ /*
+ * These should setup decrementer_count
+ */
+ if ( _machine == _MACH_Pmac )
+ pmac_calibrate_decr();
+#ifdef CONFIG_PREP
+ else /* PReP */
+ prep_calibrate_decr();
+#endif /* CONFIG_PREP */
+ }
+
+ if ( _machine == _MACH_Pmac )
+ set_rtc_time = pmac_set_rtc_time;
+#ifdef CONFIG_PREP
+ else /* prep */
+ set_rtc_time = prep_set_rtc_time;
+#endif /* CONFIG_PREP */
+
+ set_dec(decrementer_count);
+}
+
+#ifdef CONFIG_PREP
+/*
+ * Uses the on-board timer to calibrate the on-chip decrementer register
+ * for prep systems. On the pmac the OF tells us what the frequency is
+ * but on prep we have to figure it out.
+ * -- Cort
+ */
+int calibrate_done = 0;
+volatile int *done_ptr = &calibrate_done;
+void prep_calibrate_decr(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+
+#define TIMER0_COUNT 0x40
+#define TIMER_CONTROL 0x43
+ /* set timer to periodic mode */
+ outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */
+ /* set the clock to ~100 Hz */
+ outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */
+ outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */
+
+ if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0)
+ panic("Could not allocate timer IRQ!");
+ __sti();
+ while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */
+ restore_flags(flags);
+ free_irq( 0, NULL);
+}
+
+void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
+{
+ int freq, divisor;
+ static unsigned long t1 = 0, t2 = 0;
+
+ if ( !t1 )
+ t1 = get_dec();
+ else if (!t2)
+ {
+ t2 = get_dec();
+ t2 = t1-t2; /* decr's in 1/HZ */
+ t2 = t2*HZ; /* # decrs in 1s - thus in Hz */
+ freq = t2 * 60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("time_init: decrementer frequency = %d/%d (%luMHz)\n",
+ freq, divisor,t2>>20);
+ decrementer_count = freq / HZ / divisor;
+ count_period_num = divisor;
+ count_period_den = freq / 1000000;
+ *done_ptr = 1;
+ }
+}
+#endif /* CONFIG_PREP */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov