File: /usr/src/linux/arch/m68k/kernel/time.c

1     /*
2      *  linux/arch/m68k/kernel/time.c
3      *
4      *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
5      *
6      * This file contains the m68k-specific time handling details.
7      * Most of the stuff is located in the machine specific files.
8      *
9      * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
10      *		"A Kernel Model for Precision Timekeeping" by Dave Mills
11      */
12     
13     #include <linux/config.h> /* CONFIG_HEARTBEAT */
14     #include <linux/errno.h>
15     #include <linux/sched.h>
16     #include <linux/kernel.h>
17     #include <linux/param.h>
18     #include <linux/string.h>
19     #include <linux/mm.h>
20     
21     #include <asm/machdep.h>
22     #include <asm/io.h>
23     
24     #include <linux/timex.h>
25     
26     
27     static inline int set_rtc_mmss(unsigned long nowtime)
28     {
29       if (mach_set_clock_mmss)
30         return mach_set_clock_mmss (nowtime);
31       return -1;
32     }
33     
34     static inline void do_profile (unsigned long pc)
35     {
36     	if (prof_buffer && current->pid) {
37     		extern int _stext;
38     		pc -= (unsigned long) &_stext;
39     		pc >>= prof_shift;
40     		if (pc < prof_len)
41     			++prof_buffer[pc];
42     		else
43     		/*
44     		 * Don't ignore out-of-bounds PC values silently,
45     		 * put them into the last histogram slot, so if
46     		 * present, they will show up as a sharp peak.
47     		 */
48     			++prof_buffer[prof_len-1];
49     	}
50     }
51     
52     /*
53      * timer_interrupt() needs to keep up the real-time clock,
54      * as well as call the "do_timer()" routine every clocktick
55      */
56     static void timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
57     {
58     	/* last time the cmos clock got updated */
59     	static long last_rtc_update=0;
60     
61     	do_timer(regs);
62     
63     	if (!user_mode(regs))
64     		do_profile(regs->pc);
65     
66     	/*
67     	 * If we have an externally synchronized Linux clock, then update
68     	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
69     	 * called as close as possible to 500 ms before the new second starts.
70     	 */
71     	if ((time_status & STA_UNSYNC) == 0 &&
72     	    xtime.tv_sec > last_rtc_update + 660 &&
73     	    xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&
74     	    xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
75     	  if (set_rtc_mmss(xtime.tv_sec) == 0)
76     	    last_rtc_update = xtime.tv_sec;
77     	  else
78     	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
79     	}
80     #ifdef CONFIG_HEARTBEAT
81     	/* use power LED as a heartbeat instead -- much more useful
82     	   for debugging -- based on the version for PReP by Cort */
83     	/* acts like an actual heart beat -- ie thump-thump-pause... */
84     	if (mach_heartbeat) {
85     	    static unsigned cnt = 0, period = 0, dist = 0;
86     
87     	    if (cnt == 0 || cnt == dist)
88     		mach_heartbeat( 1 );
89     	    else if (cnt == 7 || cnt == dist+7)
90     		mach_heartbeat( 0 );
91     
92     	    if (++cnt > period) {
93     		cnt = 0;
94     		/* The hyperbolic function below modifies the heartbeat period
95     		 * length in dependency of the current (5min) load. It goes
96     		 * through the points f(0)=126, f(1)=86, f(5)=51,
97     		 * f(inf)->30. */
98     		period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
99     		dist = period / 4;
100     	    }
101     	}
102     #endif /* CONFIG_HEARTBEAT */
103     }
104     
105     void time_init(void)
106     {
107     	unsigned int year, mon, day, hour, min, sec;
108     
109     	extern void arch_gettod(int *year, int *mon, int *day, int *hour,
110     				int *min, int *sec);
111     
112     	arch_gettod (&year, &mon, &day, &hour, &min, &sec);
113     
114     	if ((year += 1900) < 1970)
115     		year += 100;
116     	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
117     	xtime.tv_usec = 0;
118     
119     	mach_sched_init(timer_interrupt);
120     }
121     
122     extern rwlock_t xtime_lock;
123     
124     /*
125      * This version of gettimeofday has near microsecond resolution.
126      */
127     void do_gettimeofday(struct timeval *tv)
128     {
129     	extern unsigned long wall_jiffies;
130     	unsigned long flags;
131     	unsigned long usec, sec, lost;
132     
133     	read_lock_irqsave(&xtime_lock, flags);
134     	usec = mach_gettimeoffset();
135     	lost = jiffies - wall_jiffies;
136     	if (lost)
137     		usec += lost * (1000000/HZ);
138     	sec = xtime.tv_sec;
139     	usec += xtime.tv_usec;
140     	read_unlock_irqrestore(&xtime_lock, flags);
141     
142     	while (usec >= 1000000) {
143     		usec -= 1000000;
144     		sec++;
145     	}
146     
147     	tv->tv_sec = sec;
148     	tv->tv_usec = usec;
149     }
150     
151     void do_settimeofday(struct timeval *tv)
152     {
153     	write_lock_irq(&xtime_lock);
154     	/* This is revolting. We need to set the xtime.tv_usec
155     	 * correctly. However, the value in this location is
156     	 * is value at the last tick.
157     	 * Discover what correction gettimeofday
158     	 * would have done, and then undo it!
159     	 */
160     	tv->tv_usec -= mach_gettimeoffset();
161     
162     	while (tv->tv_usec < 0) {
163     		tv->tv_usec += 1000000;
164     		tv->tv_sec--;
165     	}
166     
167     	xtime = *tv;
168     	time_adjust = 0;		/* stop active adjtime() */
169     	time_status |= STA_UNSYNC;
170     	time_maxerror = NTP_PHASE_LIMIT;
171     	time_esterror = NTP_PHASE_LIMIT;
172     	write_unlock_irq(&xtime_lock);
173     }
174