Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-drm-fsl-dcu.git] / arch / m68knommu / kernel / time.c
1 /*
2  *  linux/arch/m68knommu/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/errno.h>
14 #include <linux/module.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 #include <linux/profile.h>
21 #include <linux/time.h>
22 #include <linux/timex.h>
23
24 #include <asm/machdep.h>
25 #include <asm/io.h>
26 #include <asm/irq_regs.h>
27
28 #define TICK_SIZE (tick_nsec / 1000)
29
30
31 static inline int set_rtc_mmss(unsigned long nowtime)
32 {
33         if (mach_set_clock_mmss)
34                 return mach_set_clock_mmss (nowtime);
35         return -1;
36 }
37
38 /*
39  * timer_interrupt() needs to keep up the real-time clock,
40  * as well as call the "do_timer()" routine every clocktick
41  */
42 static irqreturn_t timer_interrupt(int irq, void *dummy)
43 {
44         /* last time the cmos clock got updated */
45         static long last_rtc_update=0;
46
47         /* may need to kick the hardware timer */
48         if (mach_tick)
49           mach_tick();
50
51         write_seqlock(&xtime_lock);
52
53         do_timer(1);
54 #ifndef CONFIG_SMP
55         update_process_times(user_mode(get_irq_regs()));
56 #endif
57         if (current->pid)
58                 profile_tick(CPU_PROFILING);
59
60         /*
61          * If we have an externally synchronized Linux clock, then update
62          * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
63          * called as close as possible to 500 ms before the new second starts.
64          */
65         if (ntp_synced() &&
66             xtime.tv_sec > last_rtc_update + 660 &&
67             (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
68             (xtime.tv_nsec  / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
69           if (set_rtc_mmss(xtime.tv_sec) == 0)
70             last_rtc_update = xtime.tv_sec;
71           else
72             last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
73         }
74 #ifdef CONFIG_HEARTBEAT
75         /* use power LED as a heartbeat instead -- much more useful
76            for debugging -- based on the version for PReP by Cort */
77         /* acts like an actual heart beat -- ie thump-thump-pause... */
78         if (mach_heartbeat) {
79             static unsigned cnt = 0, period = 0, dist = 0;
80
81             if (cnt == 0 || cnt == dist)
82                 mach_heartbeat( 1 );
83             else if (cnt == 7 || cnt == dist+7)
84                 mach_heartbeat( 0 );
85
86             if (++cnt > period) {
87                 cnt = 0;
88                 /* The hyperbolic function below modifies the heartbeat period
89                  * length in dependency of the current (5min) load. It goes
90                  * through the points f(0)=126, f(1)=86, f(5)=51,
91                  * f(inf)->30. */
92                 period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
93                 dist = period / 4;
94             }
95         }
96 #endif /* CONFIG_HEARTBEAT */
97
98         write_sequnlock(&xtime_lock);
99         return(IRQ_HANDLED);
100 }
101
102 void time_init(void)
103 {
104         unsigned int year, mon, day, hour, min, sec;
105
106         extern void arch_gettod(int *year, int *mon, int *day, int *hour,
107                                 int *min, int *sec);
108
109         arch_gettod(&year, &mon, &day, &hour, &min, &sec);
110
111         if ((year += 1900) < 1970)
112                 year += 100;
113         xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
114         xtime.tv_nsec = 0;
115         wall_to_monotonic.tv_sec = -xtime.tv_sec;
116
117         mach_sched_init(timer_interrupt);
118 }
119
120 /*
121  * This version of gettimeofday has near microsecond resolution.
122  */
123 void do_gettimeofday(struct timeval *tv)
124 {
125         unsigned long flags;
126         unsigned long seq;
127         unsigned long usec, sec;
128
129         do {
130                 seq = read_seqbegin_irqsave(&xtime_lock, flags);
131                 usec = mach_gettimeoffset ? mach_gettimeoffset() : 0;
132                 sec = xtime.tv_sec;
133                 usec += (xtime.tv_nsec / 1000);
134         } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
135
136         while (usec >= 1000000) {
137                 usec -= 1000000;
138                 sec++;
139         }
140
141         tv->tv_sec = sec;
142         tv->tv_usec = usec;
143 }
144
145 EXPORT_SYMBOL(do_gettimeofday);
146
147 int do_settimeofday(struct timespec *tv)
148 {
149         time_t wtm_sec, sec = tv->tv_sec;
150         long wtm_nsec, nsec = tv->tv_nsec;
151
152         if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
153                 return -EINVAL;
154
155         write_seqlock_irq(&xtime_lock);
156         /*
157          * This is revolting. We need to set the xtime.tv_usec
158          * correctly. However, the value in this location is
159          * is value at the last tick.
160          * Discover what correction gettimeofday
161          * would have done, and then undo it!
162          */
163         if (mach_gettimeoffset)
164                 nsec -= (mach_gettimeoffset() * 1000);
165
166         wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
167         wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
168
169         set_normalized_timespec(&xtime, sec, nsec);
170         set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
171
172         ntp_clear();
173         write_sequnlock_irq(&xtime_lock);
174         clock_was_set();
175         return 0;
176 }
177 EXPORT_SYMBOL(do_settimeofday);