2 * This code implements timer_ops for the cyclone counter found
3 * on IBM x440, x360, and other Summit based systems.
5 * Copyright (C) 2002 IBM, John Stultz (johnstul@us.ibm.com)
9 #include <linux/spinlock.h>
10 #include <linux/init.h>
11 #include <linux/timex.h>
12 #include <linux/errno.h>
13 #include <linux/string.h>
14 #include <linux/jiffies.h>
16 #include <asm/timer.h>
18 #include <asm/pgtable.h>
19 #include <asm/fixmap.h>
22 extern spinlock_t i8253_lock
;
24 /* Number of usecs that the last interrupt was delayed */
25 static int delay_at_last_interrupt
;
27 #define CYCLONE_CBAR_ADDR 0xFEB00CD0
28 #define CYCLONE_PMCC_OFFSET 0x51A0
29 #define CYCLONE_MPMC_OFFSET 0x51D0
30 #define CYCLONE_MPCS_OFFSET 0x51A8
31 #define CYCLONE_TIMER_FREQ 100000000
32 #define CYCLONE_TIMER_MASK (((u64)1<<40)-1) /* 40 bit mask */
35 static u32
* volatile cyclone_timer
; /* Cyclone MPMC0 register */
36 static u32 last_cyclone_low
;
37 static u32 last_cyclone_high
;
38 static unsigned long long monotonic_base
;
39 static seqlock_t monotonic_lock
= SEQLOCK_UNLOCKED
;
41 /* helper macro to atomically read both cyclone counter registers */
42 #define read_cyclone_counter(low,high) \
44 high = cyclone_timer[1]; low = cyclone_timer[0]; \
45 } while (high != cyclone_timer[1]);
48 static void mark_offset_cyclone(void)
50 unsigned long lost
, delay
;
51 unsigned long delta
= last_cyclone_low
;
53 unsigned long long this_offset
, last_offset
;
55 write_seqlock(&monotonic_lock
);
56 last_offset
= ((unsigned long long)last_cyclone_high
<<32)|last_cyclone_low
;
58 spin_lock(&i8253_lock
);
59 read_cyclone_counter(last_cyclone_low
,last_cyclone_high
);
61 /* read values for delay_at_last_interrupt */
62 outb_p(0x00, 0x43); /* latch the count ASAP */
64 count
= inb_p(0x40); /* read the latched count */
65 count
|= inb(0x40) << 8;
68 * VIA686a test code... reset the latch if count > max + 1
69 * from timer_pit.c - cjb
72 outb_p(0x34, PIT_MODE
);
73 outb_p(LATCH
& 0xff, PIT_CH0
);
74 outb(LATCH
>> 8, PIT_CH0
);
77 spin_unlock(&i8253_lock
);
79 /* lost tick compensation */
80 delta
= last_cyclone_low
- delta
;
81 delta
/= (CYCLONE_TIMER_FREQ
/1000000);
82 delta
+= delay_at_last_interrupt
;
83 lost
= delta
/(1000000/HZ
);
84 delay
= delta
%(1000000/HZ
);
88 /* update the monotonic base value */
89 this_offset
= ((unsigned long long)last_cyclone_high
<<32)|last_cyclone_low
;
90 monotonic_base
+= (this_offset
- last_offset
) & CYCLONE_TIMER_MASK
;
91 write_sequnlock(&monotonic_lock
);
93 /* calculate delay_at_last_interrupt */
94 count
= ((LATCH
-1) - count
) * TICK_SIZE
;
95 delay_at_last_interrupt
= (count
+ LATCH
/2) / LATCH
;
98 /* catch corner case where tick rollover occured
99 * between cyclone and pit reads (as noted when
100 * usec delta is > 90% # of usecs/tick)
102 if (lost
&& abs(delay
- delay_at_last_interrupt
) > (900000/HZ
))
106 static unsigned long get_offset_cyclone(void)
111 return delay_at_last_interrupt
;
113 /* Read the cyclone timer */
114 offset
= cyclone_timer
[0];
116 /* .. relative to previous jiffy */
117 offset
= offset
- last_cyclone_low
;
119 /* convert cyclone ticks to microseconds */
120 /* XXX slow, can we speed this up? */
121 offset
= offset
/(CYCLONE_TIMER_FREQ
/1000000);
123 /* our adjusted time offset in microseconds */
124 return delay_at_last_interrupt
+ offset
;
127 static unsigned long long monotonic_clock_cyclone(void)
129 u32 now_low
, now_high
;
130 unsigned long long last_offset
, this_offset
, base
;
131 unsigned long long ret
;
134 /* atomically read monotonic base & last_offset */
136 seq
= read_seqbegin(&monotonic_lock
);
137 last_offset
= ((unsigned long long)last_cyclone_high
<<32)|last_cyclone_low
;
138 base
= monotonic_base
;
139 } while (read_seqretry(&monotonic_lock
, seq
));
142 /* Read the cyclone counter */
143 read_cyclone_counter(now_low
,now_high
);
144 this_offset
= ((unsigned long long)now_high
<<32)|now_low
;
146 /* convert to nanoseconds */
147 ret
= base
+ ((this_offset
- last_offset
)&CYCLONE_TIMER_MASK
);
148 return ret
* (1000000000 / CYCLONE_TIMER_FREQ
);
151 static int __init
init_cyclone(char* override
)
154 u32 base
; /* saved cyclone base address */
155 u32 pageaddr
; /* page that contains cyclone_timer register */
156 u32 offset
; /* offset from pageaddr to cyclone_timer register */
159 /* check clock override */
160 if (override
[0] && strncmp(override
,"cyclone",7))
163 /*make sure we're on a summit box*/
164 if(!use_cyclone
) return -ENODEV
;
166 printk(KERN_INFO
"Summit chipset: Starting Cyclone Counter.\n");
168 /* find base address */
169 pageaddr
= (CYCLONE_CBAR_ADDR
)&PAGE_MASK
;
170 offset
= (CYCLONE_CBAR_ADDR
)&(~PAGE_MASK
);
171 set_fixmap_nocache(FIX_CYCLONE_TIMER
, pageaddr
);
172 reg
= (u32
*)(fix_to_virt(FIX_CYCLONE_TIMER
) + offset
);
174 printk(KERN_ERR
"Summit chipset: Could not find valid CBAR register.\n");
179 printk(KERN_ERR
"Summit chipset: Could not find valid CBAR value.\n");
184 pageaddr
= (base
+ CYCLONE_PMCC_OFFSET
)&PAGE_MASK
;
185 offset
= (base
+ CYCLONE_PMCC_OFFSET
)&(~PAGE_MASK
);
186 set_fixmap_nocache(FIX_CYCLONE_TIMER
, pageaddr
);
187 reg
= (u32
*)(fix_to_virt(FIX_CYCLONE_TIMER
) + offset
);
189 printk(KERN_ERR
"Summit chipset: Could not find valid PMCC register.\n");
195 pageaddr
= (base
+ CYCLONE_MPCS_OFFSET
)&PAGE_MASK
;
196 offset
= (base
+ CYCLONE_MPCS_OFFSET
)&(~PAGE_MASK
);
197 set_fixmap_nocache(FIX_CYCLONE_TIMER
, pageaddr
);
198 reg
= (u32
*)(fix_to_virt(FIX_CYCLONE_TIMER
) + offset
);
200 printk(KERN_ERR
"Summit chipset: Could not find valid MPCS register.\n");
205 /* map in cyclone_timer */
206 pageaddr
= (base
+ CYCLONE_MPMC_OFFSET
)&PAGE_MASK
;
207 offset
= (base
+ CYCLONE_MPMC_OFFSET
)&(~PAGE_MASK
);
208 set_fixmap_nocache(FIX_CYCLONE_TIMER
, pageaddr
);
209 cyclone_timer
= (u32
*)(fix_to_virt(FIX_CYCLONE_TIMER
) + offset
);
211 printk(KERN_ERR
"Summit chipset: Could not find valid MPMC register.\n");
215 /*quick test to make sure its ticking*/
217 u32 old
= cyclone_timer
[0];
219 while(stall
--) barrier();
220 if(cyclone_timer
[0] == old
){
221 printk(KERN_ERR
"Summit chipset: Counter not counting! DISABLED\n");
229 /* Everything looks good! */
234 static void delay_cyclone(unsigned long loops
)
236 unsigned long bclock
, now
;
239 bclock
= cyclone_timer
[0];
242 now
= cyclone_timer
[0];
243 } while ((now
-bclock
) < loops
);
245 /************************************************************/
247 /* cyclone timer_opts struct */
248 static struct timer_opts timer_cyclone
= {
250 .mark_offset
= mark_offset_cyclone
,
251 .get_offset
= get_offset_cyclone
,
252 .monotonic_clock
= monotonic_clock_cyclone
,
253 .delay
= delay_cyclone
,
256 struct init_timer_opts __initdata timer_cyclone_init
= {
257 .init
= init_cyclone
,
258 .opts
= &timer_cyclone
,