1 /*
2 * linux/arch/ia64/sn/kernel/sn2/timer.c
3 *
4 * Copyright (C) 2003 Silicon Graphics, Inc.
5 */
6
7 #include <linux/init.h>
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/time.h>
11 #include <linux/interrupt.h>
12
13 #include <asm/hw_irq.h>
14 #include <asm/system.h>
15
16 #include <asm/sn/leds.h>
17 #include <asm/sn/clksupport.h>
18
19
20 extern unsigned long sn_rtc_cycles_per_second;
21 static volatile unsigned long last_wall_rtc;
22
23 /**
24 * gettimeoffset - number of usecs elapsed since &xtime was last updated
25 *
26 * This function is used by do_gettimeofday() to determine the number
27 * of usecs that have elapsed since the last update to &xtime. On SN
28 * this is accomplished using the RTC built in to each Hub chip; each
29 * is guaranteed to be synchronized by the PROM, so a local read will
30 * suffice (GET_RTC_COUNTER() does this for us). A snapshot of the RTC
31 * value is taken every time wall_jiffies is updated by the
32 * update_wall_time_hook (sn2_update_wall_time) which means we don't
33 * have to adjust for lost jiffies ticks or anything like that.
34 */
35 extern unsigned long wall_jiffies; /* from kernel/timer.c */
36
37 static volatile long rtc_offset __cacheline_aligned;
38 static long rtc_cycles_per_usec;
39 static long rtc_per_timer_tick;
40
41 unsigned long
sn_gettimeoffset(void)42 sn_gettimeoffset(void)
43 {
44 long current_rtc, elapsed_rtc, old, new_offset;
45
46 do {
47 old = rtc_offset;
48 current_rtc = GET_RTC_COUNTER();
49
50 /*
51 * Need to address wrapping here!
52 */
53 elapsed_rtc = (long)(current_rtc - last_wall_rtc);
54
55 /*
56 * This case is non lethal as the max() will take care of it.
57 */
58 #if 0
59 if (elapsed_rtc < 0) {
60 printk(KERN_ERR "sn_gettimeoffset(): time goes "
61 "backwards!\n current_rtc 0x%016lx, "
62 "last_wall_rtc 0x%016lx, elapsed %08lx, "
63 "offset %li\n", current_rtc, last_wall_rtc,
64 elapsed_rtc, max(elapsed_rtc, rtc_offset)/
65 rtc_cycles_per_usec);
66 }
67 #endif
68
69 new_offset = max(elapsed_rtc, old);
70 } while (cmpxchg(&rtc_offset, old, new_offset) != old);
71
72 return new_offset / rtc_cycles_per_usec;
73 }
74
75
sn2_update_wall_time(void)76 void sn2_update_wall_time(void)
77 {
78 rtc_offset -= min(rtc_offset, rtc_per_timer_tick);
79 last_wall_rtc = GET_RTC_COUNTER();
80 }
81
82
sn2_reset_wall_time(void)83 void sn2_reset_wall_time(void)
84 {
85 rtc_offset = 0;
86 last_wall_rtc = GET_RTC_COUNTER();
87 }
88
89 void __init
sn_timer_init(void)90 sn_timer_init(void)
91 {
92 rtc_per_timer_tick = sn_rtc_cycles_per_second / HZ;
93 rtc_cycles_per_usec = sn_rtc_cycles_per_second / 1000000;
94
95 last_wall_rtc = GET_RTC_COUNTER();
96 update_wall_time_hook = sn2_update_wall_time;
97 reset_wall_time_hook = sn2_reset_wall_time;
98 gettimeoffset = sn_gettimeoffset;
99 }
100