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>
21 extern spinlock_t i8253_lock;
23 /* Number of usecs that the last interrupt was delayed */
24 static int delay_at_last_interrupt;
26 #define CYCLONE_CBAR_ADDR 0xFEB00CD0
27 #define CYCLONE_PMCC_OFFSET 0x51A0
28 #define CYCLONE_MPMC_OFFSET 0x51D0
29 #define CYCLONE_MPCS_OFFSET 0x51A8
30 #define CYCLONE_TIMER_FREQ 100000000
31 #define CYCLONE_TIMER_MASK (((u64)1<<40)-1) /* 40 bit mask */
34 static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */
35 static u32 last_cyclone_low;
36 static u32 last_cyclone_high;
37 static unsigned long long monotonic_base;
38 static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
40 /* helper macro to atomically read both cyclone counter registers */
41 #define read_cyclone_counter(low,high) \
43 high = cyclone_timer[1]; low = cyclone_timer[0]; \
44 } while (high != cyclone_timer[1]);
47 static void mark_offset_cyclone(void)
49 unsigned long lost, delay;
50 unsigned long delta = last_cyclone_low;
52 unsigned long long this_offset, last_offset;
54 write_seqlock(&monotonic_lock);
55 last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
57 spin_lock(&i8253_lock);
58 read_cyclone_counter(last_cyclone_low,last_cyclone_high);
60 /* read values for delay_at_last_interrupt */
61 outb_p(0x00, 0x43); /* latch the count ASAP */
63 count = inb_p(0x40); /* read the latched count */
64 count |= inb(0x40) << 8;
65 spin_unlock(&i8253_lock);
67 /* lost tick compensation */
68 delta = last_cyclone_low - delta;
69 delta /= (CYCLONE_TIMER_FREQ/1000000);
70 delta += delay_at_last_interrupt;
71 lost = delta/(1000000/HZ);
72 delay = delta%(1000000/HZ);
76 /* update the monotonic base value */
77 this_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
78 monotonic_base += (this_offset - last_offset) & CYCLONE_TIMER_MASK;
79 write_sequnlock(&monotonic_lock);
81 /* calculate delay_at_last_interrupt */
82 count = ((LATCH-1) - count) * TICK_SIZE;
83 delay_at_last_interrupt = (count + LATCH/2) / LATCH;
86 /* catch corner case where tick rollover occured
87 * between cyclone and pit reads (as noted when
88 * usec delta is > 90% # of usecs/tick)
90 if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
94 static unsigned long get_offset_cyclone(void)
99 return delay_at_last_interrupt;
101 /* Read the cyclone timer */
102 offset = cyclone_timer[0];
104 /* .. relative to previous jiffy */
105 offset = offset - last_cyclone_low;
107 /* convert cyclone ticks to microseconds */
108 /* XXX slow, can we speed this up? */
109 offset = offset/(CYCLONE_TIMER_FREQ/1000000);
111 /* our adjusted time offset in microseconds */
112 return delay_at_last_interrupt + offset;
115 static unsigned long long monotonic_clock_cyclone(void)
117 u32 now_low, now_high;
118 unsigned long long last_offset, this_offset, base;
119 unsigned long long ret;
122 /* atomically read monotonic base & last_offset */
124 seq = read_seqbegin(&monotonic_lock);
125 last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
126 base = monotonic_base;
127 } while (read_seqretry(&monotonic_lock, seq));
130 /* Read the cyclone counter */
131 read_cyclone_counter(now_low,now_high);
132 this_offset = ((unsigned long long)now_high<<32)|now_low;
134 /* convert to nanoseconds */
135 ret = base + ((this_offset - last_offset)&CYCLONE_TIMER_MASK);
136 return ret * (1000000000 / CYCLONE_TIMER_FREQ);
139 static int __init init_cyclone(char* override)
142 u32 base; /* saved cyclone base address */
143 u32 pageaddr; /* page that contains cyclone_timer register */
144 u32 offset; /* offset from pageaddr to cyclone_timer register */
147 /* check clock override */
148 if (override[0] && strncmp(override,"cyclone",7))
151 /*make sure we're on a summit box*/
152 if(!use_cyclone) return -ENODEV;
154 printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
156 /* find base address */
157 pageaddr = (CYCLONE_CBAR_ADDR)&PAGE_MASK;
158 offset = (CYCLONE_CBAR_ADDR)&(~PAGE_MASK);
159 set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
160 reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
162 printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
167 printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
172 pageaddr = (base + CYCLONE_PMCC_OFFSET)&PAGE_MASK;
173 offset = (base + CYCLONE_PMCC_OFFSET)&(~PAGE_MASK);
174 set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
175 reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
177 printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
183 pageaddr = (base + CYCLONE_MPCS_OFFSET)&PAGE_MASK;
184 offset = (base + CYCLONE_MPCS_OFFSET)&(~PAGE_MASK);
185 set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
186 reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
188 printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
193 /* map in cyclone_timer */
194 pageaddr = (base + CYCLONE_MPMC_OFFSET)&PAGE_MASK;
195 offset = (base + CYCLONE_MPMC_OFFSET)&(~PAGE_MASK);
196 set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
197 cyclone_timer = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
199 printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
203 /*quick test to make sure its ticking*/
205 u32 old = cyclone_timer[0];
207 while(stall--) barrier();
208 if(cyclone_timer[0] == old){
209 printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
217 /* Everything looks good! */
222 static void delay_cyclone(unsigned long loops)
224 unsigned long bclock, now;
227 bclock = cyclone_timer[0];
230 now = cyclone_timer[0];
231 } while ((now-bclock) < loops);
233 /************************************************************/
235 /* cyclone timer_opts struct */
236 struct timer_opts timer_cyclone = {
238 .init = init_cyclone,
239 .mark_offset = mark_offset_cyclone,
240 .get_offset = get_offset_cyclone,
241 .monotonic_clock = monotonic_clock_cyclone,
242 .delay = delay_cyclone,