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