Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* time.c: FRV arch-specific time handling |
2 | * | |
3 | * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. | |
4 | * Written by David Howells (dhowells@redhat.com) | |
5 | * - Derived from arch/m68k/kernel/time.c | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation; either version | |
10 | * 2 of the License, or (at your option) any later version. | |
11 | */ | |
12 | ||
13 | #include <linux/config.h> /* CONFIG_HEARTBEAT */ | |
14 | #include <linux/module.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/sched.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/param.h> | |
19 | #include <linux/string.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/profile.h> | |
22 | #include <linux/irq.h> | |
23 | #include <linux/mm.h> | |
24 | ||
25 | #include <asm/io.h> | |
26 | #include <asm/timer-regs.h> | |
27 | #include <asm/mb-regs.h> | |
28 | #include <asm/mb86943a.h> | |
29 | #include <asm/irq-routing.h> | |
30 | ||
31 | #include <linux/timex.h> | |
32 | ||
33 | #define TICK_SIZE (tick_nsec / 1000) | |
34 | ||
35 | extern unsigned long wall_jiffies; | |
36 | ||
1da177e4 LT |
37 | unsigned long __nongprelbss __clkin_clock_speed_HZ; |
38 | unsigned long __nongprelbss __ext_bus_clock_speed_HZ; | |
39 | unsigned long __nongprelbss __res_bus_clock_speed_HZ; | |
40 | unsigned long __nongprelbss __sdram_clock_speed_HZ; | |
41 | unsigned long __nongprelbss __core_bus_clock_speed_HZ; | |
42 | unsigned long __nongprelbss __core_clock_speed_HZ; | |
43 | unsigned long __nongprelbss __dsu_clock_speed_HZ; | |
44 | unsigned long __nongprelbss __serial_clock_speed_HZ; | |
45 | unsigned long __delay_loops_MHz; | |
46 | ||
47 | static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs *regs); | |
48 | ||
49 | static struct irqaction timer_irq = { | |
50 | timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL | |
51 | }; | |
52 | ||
53 | static inline int set_rtc_mmss(unsigned long nowtime) | |
54 | { | |
55 | return -1; | |
56 | } | |
57 | ||
58 | /* | |
59 | * timer_interrupt() needs to keep up the real-time clock, | |
60 | * as well as call the "do_timer()" routine every clocktick | |
61 | */ | |
62 | static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs) | |
63 | { | |
64 | /* last time the cmos clock got updated */ | |
65 | static long last_rtc_update = 0; | |
66 | ||
67 | /* | |
68 | * Here we are in the timer irq handler. We just have irqs locally | |
69 | * disabled but we don't know if the timer_bh is running on the other | |
70 | * CPU. We need to avoid to SMP race with it. NOTE: we don' t need | |
71 | * the irq version of write_lock because as just said we have irq | |
72 | * locally disabled. -arca | |
73 | */ | |
74 | write_seqlock(&xtime_lock); | |
75 | ||
76 | do_timer(regs); | |
77 | update_process_times(user_mode(regs)); | |
78 | profile_tick(CPU_PROFILING, regs); | |
79 | ||
80 | /* | |
81 | * If we have an externally synchronized Linux clock, then update | |
82 | * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | |
83 | * called as close as possible to 500 ms before the new second starts. | |
84 | */ | |
b149ee22 | 85 | if (ntp_synced() && |
1da177e4 LT |
86 | xtime.tv_sec > last_rtc_update + 660 && |
87 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | |
88 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2 | |
89 | ) { | |
90 | if (set_rtc_mmss(xtime.tv_sec) == 0) | |
91 | last_rtc_update = xtime.tv_sec; | |
92 | else | |
93 | last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ | |
94 | } | |
95 | ||
96 | #ifdef CONFIG_HEARTBEAT | |
97 | static unsigned short n; | |
98 | n++; | |
99 | __set_LEDS(n); | |
100 | #endif /* CONFIG_HEARTBEAT */ | |
101 | ||
102 | write_sequnlock(&xtime_lock); | |
103 | return IRQ_HANDLED; | |
104 | } | |
105 | ||
106 | void time_divisor_init(void) | |
107 | { | |
108 | unsigned short base, pre, prediv; | |
109 | ||
110 | /* set the scheduling timer going */ | |
111 | pre = 1; | |
112 | prediv = 4; | |
113 | base = __res_bus_clock_speed_HZ / pre / HZ / (1 << prediv); | |
114 | ||
115 | __set_TPRV(pre); | |
116 | __set_TxCKSL_DATA(0, prediv); | |
117 | __set_TCTR(TCTR_SC_CTR0 | TCTR_RL_RW_LH8 | TCTR_MODE_2); | |
118 | __set_TCSR_DATA(0, base & 0xff); | |
119 | __set_TCSR_DATA(0, base >> 8); | |
120 | } | |
121 | ||
122 | void time_init(void) | |
123 | { | |
124 | unsigned int year, mon, day, hour, min, sec; | |
125 | ||
126 | extern void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec); | |
127 | ||
128 | /* FIX by dqg : Set to zero for platforms that don't have tod */ | |
129 | /* without this time is undefined and can overflow time_t, causing */ | |
130 | /* very stange errors */ | |
131 | year = 1980; | |
132 | mon = day = 1; | |
133 | hour = min = sec = 0; | |
134 | arch_gettod (&year, &mon, &day, &hour, &min, &sec); | |
135 | ||
136 | if ((year += 1900) < 1970) | |
137 | year += 100; | |
138 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | |
139 | xtime.tv_nsec = 0; | |
140 | ||
141 | /* install scheduling interrupt handler */ | |
142 | setup_irq(IRQ_CPU_TIMER0, &timer_irq); | |
143 | ||
144 | time_divisor_init(); | |
145 | } | |
146 | ||
147 | /* | |
148 | * This version of gettimeofday has near microsecond resolution. | |
149 | */ | |
150 | void do_gettimeofday(struct timeval *tv) | |
151 | { | |
152 | unsigned long seq; | |
153 | unsigned long usec, sec; | |
154 | unsigned long max_ntp_tick; | |
155 | ||
156 | do { | |
157 | unsigned long lost; | |
158 | ||
159 | seq = read_seqbegin(&xtime_lock); | |
160 | ||
161 | usec = 0; | |
162 | lost = jiffies - wall_jiffies; | |
163 | ||
164 | /* | |
165 | * If time_adjust is negative then NTP is slowing the clock | |
166 | * so make sure not to go into next possible interval. | |
167 | * Better to lose some accuracy than have time go backwards.. | |
168 | */ | |
169 | if (unlikely(time_adjust < 0)) { | |
170 | max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj; | |
171 | usec = min(usec, max_ntp_tick); | |
172 | ||
173 | if (lost) | |
174 | usec += lost * max_ntp_tick; | |
175 | } | |
176 | else if (unlikely(lost)) | |
177 | usec += lost * (USEC_PER_SEC / HZ); | |
178 | ||
179 | sec = xtime.tv_sec; | |
180 | usec += (xtime.tv_nsec / 1000); | |
181 | } while (read_seqretry(&xtime_lock, seq)); | |
182 | ||
183 | while (usec >= 1000000) { | |
184 | usec -= 1000000; | |
185 | sec++; | |
186 | } | |
187 | ||
188 | tv->tv_sec = sec; | |
189 | tv->tv_usec = usec; | |
190 | } | |
191 | ||
40234401 DH |
192 | EXPORT_SYMBOL(do_gettimeofday); |
193 | ||
1da177e4 LT |
194 | int do_settimeofday(struct timespec *tv) |
195 | { | |
196 | time_t wtm_sec, sec = tv->tv_sec; | |
197 | long wtm_nsec, nsec = tv->tv_nsec; | |
198 | ||
199 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | |
200 | return -EINVAL; | |
201 | ||
202 | write_seqlock_irq(&xtime_lock); | |
203 | /* | |
204 | * This is revolting. We need to set "xtime" correctly. However, the | |
205 | * value in this location is the value at the most recent update of | |
206 | * wall time. Discover what correction gettimeofday() would have | |
207 | * made, and then undo it! | |
208 | */ | |
209 | nsec -= 0 * NSEC_PER_USEC; | |
210 | nsec -= (jiffies - wall_jiffies) * TICK_NSEC; | |
211 | ||
212 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | |
213 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | |
214 | ||
215 | set_normalized_timespec(&xtime, sec, nsec); | |
216 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | |
217 | ||
b149ee22 | 218 | ntp_clear(); |
1da177e4 LT |
219 | write_sequnlock_irq(&xtime_lock); |
220 | clock_was_set(); | |
221 | return 0; | |
222 | } | |
40234401 | 223 | |
943eae03 | 224 | EXPORT_SYMBOL(do_settimeofday); |
1da177e4 LT |
225 | |
226 | /* | |
227 | * Scheduler clock - returns current time in nanosec units. | |
228 | */ | |
229 | unsigned long long sched_clock(void) | |
230 | { | |
231 | return jiffies_64 * (1000000000 / HZ); | |
232 | } |