diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/kernel/time.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/kernel/time.c')
-rw-r--r-- | arch/arm/kernel/time.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c new file mode 100644 index 000000000000..c232f24f4a60 --- /dev/null +++ b/arch/arm/kernel/time.c | |||
@@ -0,0 +1,402 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
5 | * Modifications for ARM (C) 1994-2001 Russell King | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This file contains the ARM-specific time handling details: | ||
12 | * reading the RTC at bootup, etc... | ||
13 | * | ||
14 | * 1994-07-02 Alan Modra | ||
15 | * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime | ||
16 | * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 | ||
17 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
18 | */ | ||
19 | #include <linux/config.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/smp.h> | ||
26 | #include <linux/timex.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/profile.h> | ||
29 | #include <linux/sysdev.h> | ||
30 | #include <linux/timer.h> | ||
31 | |||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/irq.h> | ||
35 | #include <asm/leds.h> | ||
36 | #include <asm/thread_info.h> | ||
37 | #include <asm/mach/time.h> | ||
38 | |||
39 | u64 jiffies_64 = INITIAL_JIFFIES; | ||
40 | |||
41 | EXPORT_SYMBOL(jiffies_64); | ||
42 | |||
43 | /* | ||
44 | * Our system timer. | ||
45 | */ | ||
46 | struct sys_timer *system_timer; | ||
47 | |||
48 | extern unsigned long wall_jiffies; | ||
49 | |||
50 | /* this needs a better home */ | ||
51 | DEFINE_SPINLOCK(rtc_lock); | ||
52 | |||
53 | #ifdef CONFIG_SA1100_RTC_MODULE | ||
54 | EXPORT_SYMBOL(rtc_lock); | ||
55 | #endif | ||
56 | |||
57 | /* change this if you have some constant time drift */ | ||
58 | #define USECS_PER_JIFFY (1000000/HZ) | ||
59 | |||
60 | #ifdef CONFIG_SMP | ||
61 | unsigned long profile_pc(struct pt_regs *regs) | ||
62 | { | ||
63 | unsigned long fp, pc = instruction_pointer(regs); | ||
64 | |||
65 | if (in_lock_functions(pc)) { | ||
66 | fp = regs->ARM_fp; | ||
67 | pc = pc_pointer(((unsigned long *)fp)[-1]); | ||
68 | } | ||
69 | |||
70 | return pc; | ||
71 | } | ||
72 | EXPORT_SYMBOL(profile_pc); | ||
73 | #endif | ||
74 | |||
75 | /* | ||
76 | * hook for setting the RTC's idea of the current time. | ||
77 | */ | ||
78 | int (*set_rtc)(void); | ||
79 | |||
80 | static unsigned long dummy_gettimeoffset(void) | ||
81 | { | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Scheduler clock - returns current time in nanosec units. | ||
87 | * This is the default implementation. Sub-architecture | ||
88 | * implementations can override this. | ||
89 | */ | ||
90 | unsigned long long __attribute__((weak)) sched_clock(void) | ||
91 | { | ||
92 | return (unsigned long long)jiffies * (1000000000 / HZ); | ||
93 | } | ||
94 | |||
95 | static unsigned long next_rtc_update; | ||
96 | |||
97 | /* | ||
98 | * If we have an externally synchronized linux clock, then update | ||
99 | * CMOS clock accordingly every ~11 minutes. set_rtc() has to be | ||
100 | * called as close as possible to 500 ms before the new second | ||
101 | * starts. | ||
102 | */ | ||
103 | static inline void do_set_rtc(void) | ||
104 | { | ||
105 | if (time_status & STA_UNSYNC || set_rtc == NULL) | ||
106 | return; | ||
107 | |||
108 | if (next_rtc_update && | ||
109 | time_before((unsigned long)xtime.tv_sec, next_rtc_update)) | ||
110 | return; | ||
111 | |||
112 | if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) && | ||
113 | xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1)) | ||
114 | return; | ||
115 | |||
116 | if (set_rtc()) | ||
117 | /* | ||
118 | * rtc update failed. Try again in 60s | ||
119 | */ | ||
120 | next_rtc_update = xtime.tv_sec + 60; | ||
121 | else | ||
122 | next_rtc_update = xtime.tv_sec + 660; | ||
123 | } | ||
124 | |||
125 | #ifdef CONFIG_LEDS | ||
126 | |||
127 | static void dummy_leds_event(led_event_t evt) | ||
128 | { | ||
129 | } | ||
130 | |||
131 | void (*leds_event)(led_event_t) = dummy_leds_event; | ||
132 | |||
133 | struct leds_evt_name { | ||
134 | const char name[8]; | ||
135 | int on; | ||
136 | int off; | ||
137 | }; | ||
138 | |||
139 | static const struct leds_evt_name evt_names[] = { | ||
140 | { "amber", led_amber_on, led_amber_off }, | ||
141 | { "blue", led_blue_on, led_blue_off }, | ||
142 | { "green", led_green_on, led_green_off }, | ||
143 | { "red", led_red_on, led_red_off }, | ||
144 | }; | ||
145 | |||
146 | static ssize_t leds_store(struct sys_device *dev, const char *buf, size_t size) | ||
147 | { | ||
148 | int ret = -EINVAL, len = strcspn(buf, " "); | ||
149 | |||
150 | if (len > 0 && buf[len] == '\0') | ||
151 | len--; | ||
152 | |||
153 | if (strncmp(buf, "claim", len) == 0) { | ||
154 | leds_event(led_claim); | ||
155 | ret = size; | ||
156 | } else if (strncmp(buf, "release", len) == 0) { | ||
157 | leds_event(led_release); | ||
158 | ret = size; | ||
159 | } else { | ||
160 | int i; | ||
161 | |||
162 | for (i = 0; i < ARRAY_SIZE(evt_names); i++) { | ||
163 | if (strlen(evt_names[i].name) != len || | ||
164 | strncmp(buf, evt_names[i].name, len) != 0) | ||
165 | continue; | ||
166 | if (strncmp(buf+len, " on", 3) == 0) { | ||
167 | leds_event(evt_names[i].on); | ||
168 | ret = size; | ||
169 | } else if (strncmp(buf+len, " off", 4) == 0) { | ||
170 | leds_event(evt_names[i].off); | ||
171 | ret = size; | ||
172 | } | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static SYSDEV_ATTR(event, 0200, NULL, leds_store); | ||
180 | |||
181 | static int leds_suspend(struct sys_device *dev, pm_message_t state) | ||
182 | { | ||
183 | leds_event(led_stop); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int leds_resume(struct sys_device *dev) | ||
188 | { | ||
189 | leds_event(led_start); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int leds_shutdown(struct sys_device *dev) | ||
194 | { | ||
195 | leds_event(led_halted); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static struct sysdev_class leds_sysclass = { | ||
200 | set_kset_name("leds"), | ||
201 | .shutdown = leds_shutdown, | ||
202 | .suspend = leds_suspend, | ||
203 | .resume = leds_resume, | ||
204 | }; | ||
205 | |||
206 | static struct sys_device leds_device = { | ||
207 | .id = 0, | ||
208 | .cls = &leds_sysclass, | ||
209 | }; | ||
210 | |||
211 | static int __init leds_init(void) | ||
212 | { | ||
213 | int ret; | ||
214 | ret = sysdev_class_register(&leds_sysclass); | ||
215 | if (ret == 0) | ||
216 | ret = sysdev_register(&leds_device); | ||
217 | if (ret == 0) | ||
218 | ret = sysdev_create_file(&leds_device, &attr_event); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | device_initcall(leds_init); | ||
223 | |||
224 | EXPORT_SYMBOL(leds_event); | ||
225 | #endif | ||
226 | |||
227 | #ifdef CONFIG_LEDS_TIMER | ||
228 | static inline void do_leds(void) | ||
229 | { | ||
230 | static unsigned int count = 50; | ||
231 | |||
232 | if (--count == 0) { | ||
233 | count = 50; | ||
234 | leds_event(led_timer); | ||
235 | } | ||
236 | } | ||
237 | #else | ||
238 | #define do_leds() | ||
239 | #endif | ||
240 | |||
241 | void do_gettimeofday(struct timeval *tv) | ||
242 | { | ||
243 | unsigned long flags; | ||
244 | unsigned long seq; | ||
245 | unsigned long usec, sec, lost; | ||
246 | |||
247 | do { | ||
248 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
249 | usec = system_timer->offset(); | ||
250 | |||
251 | lost = jiffies - wall_jiffies; | ||
252 | if (lost) | ||
253 | usec += lost * USECS_PER_JIFFY; | ||
254 | |||
255 | sec = xtime.tv_sec; | ||
256 | usec += xtime.tv_nsec / 1000; | ||
257 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | ||
258 | |||
259 | /* usec may have gone up a lot: be safe */ | ||
260 | while (usec >= 1000000) { | ||
261 | usec -= 1000000; | ||
262 | sec++; | ||
263 | } | ||
264 | |||
265 | tv->tv_sec = sec; | ||
266 | tv->tv_usec = usec; | ||
267 | } | ||
268 | |||
269 | EXPORT_SYMBOL(do_gettimeofday); | ||
270 | |||
271 | int do_settimeofday(struct timespec *tv) | ||
272 | { | ||
273 | time_t wtm_sec, sec = tv->tv_sec; | ||
274 | long wtm_nsec, nsec = tv->tv_nsec; | ||
275 | |||
276 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | ||
277 | return -EINVAL; | ||
278 | |||
279 | write_seqlock_irq(&xtime_lock); | ||
280 | /* | ||
281 | * This is revolting. We need to set "xtime" correctly. However, the | ||
282 | * value in this location is the value at the most recent update of | ||
283 | * wall time. Discover what correction gettimeofday() would have | ||
284 | * done, and then undo it! | ||
285 | */ | ||
286 | nsec -= system_timer->offset() * NSEC_PER_USEC; | ||
287 | nsec -= (jiffies - wall_jiffies) * TICK_NSEC; | ||
288 | |||
289 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
290 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
291 | |||
292 | set_normalized_timespec(&xtime, sec, nsec); | ||
293 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
294 | |||
295 | time_adjust = 0; /* stop active adjtime() */ | ||
296 | time_status |= STA_UNSYNC; | ||
297 | time_maxerror = NTP_PHASE_LIMIT; | ||
298 | time_esterror = NTP_PHASE_LIMIT; | ||
299 | write_sequnlock_irq(&xtime_lock); | ||
300 | clock_was_set(); | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | EXPORT_SYMBOL(do_settimeofday); | ||
305 | |||
306 | /** | ||
307 | * save_time_delta - Save the offset between system time and RTC time | ||
308 | * @delta: pointer to timespec to store delta | ||
309 | * @rtc: pointer to timespec for current RTC time | ||
310 | * | ||
311 | * Return a delta between the system time and the RTC time, such | ||
312 | * that system time can be restored later with restore_time_delta() | ||
313 | */ | ||
314 | void save_time_delta(struct timespec *delta, struct timespec *rtc) | ||
315 | { | ||
316 | set_normalized_timespec(delta, | ||
317 | xtime.tv_sec - rtc->tv_sec, | ||
318 | xtime.tv_nsec - rtc->tv_nsec); | ||
319 | } | ||
320 | EXPORT_SYMBOL(save_time_delta); | ||
321 | |||
322 | /** | ||
323 | * restore_time_delta - Restore the current system time | ||
324 | * @delta: delta returned by save_time_delta() | ||
325 | * @rtc: pointer to timespec for current RTC time | ||
326 | */ | ||
327 | void restore_time_delta(struct timespec *delta, struct timespec *rtc) | ||
328 | { | ||
329 | struct timespec ts; | ||
330 | |||
331 | set_normalized_timespec(&ts, | ||
332 | delta->tv_sec + rtc->tv_sec, | ||
333 | delta->tv_nsec + rtc->tv_nsec); | ||
334 | |||
335 | do_settimeofday(&ts); | ||
336 | } | ||
337 | EXPORT_SYMBOL(restore_time_delta); | ||
338 | |||
339 | /* | ||
340 | * Kernel system timer support. | ||
341 | */ | ||
342 | void timer_tick(struct pt_regs *regs) | ||
343 | { | ||
344 | profile_tick(CPU_PROFILING, regs); | ||
345 | do_leds(); | ||
346 | do_set_rtc(); | ||
347 | do_timer(regs); | ||
348 | #ifndef CONFIG_SMP | ||
349 | update_process_times(user_mode(regs)); | ||
350 | #endif | ||
351 | } | ||
352 | |||
353 | #ifdef CONFIG_PM | ||
354 | static int timer_suspend(struct sys_device *dev, pm_message_t state) | ||
355 | { | ||
356 | struct sys_timer *timer = container_of(dev, struct sys_timer, dev); | ||
357 | |||
358 | if (timer->suspend != NULL) | ||
359 | timer->suspend(); | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int timer_resume(struct sys_device *dev) | ||
365 | { | ||
366 | struct sys_timer *timer = container_of(dev, struct sys_timer, dev); | ||
367 | |||
368 | if (timer->resume != NULL) | ||
369 | timer->resume(); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | #else | ||
374 | #define timer_suspend NULL | ||
375 | #define timer_resume NULL | ||
376 | #endif | ||
377 | |||
378 | static struct sysdev_class timer_sysclass = { | ||
379 | set_kset_name("timer"), | ||
380 | .suspend = timer_suspend, | ||
381 | .resume = timer_resume, | ||
382 | }; | ||
383 | |||
384 | static int __init timer_init_sysfs(void) | ||
385 | { | ||
386 | int ret = sysdev_class_register(&timer_sysclass); | ||
387 | if (ret == 0) { | ||
388 | system_timer->dev.cls = &timer_sysclass; | ||
389 | ret = sysdev_register(&system_timer->dev); | ||
390 | } | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | device_initcall(timer_init_sysfs); | ||
395 | |||
396 | void __init time_init(void) | ||
397 | { | ||
398 | if (system_timer->offset == NULL) | ||
399 | system_timer->offset = dummy_gettimeoffset; | ||
400 | system_timer->init(); | ||
401 | } | ||
402 | |||