ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / i386 / kernel / timers / timer_cyclone.c
1 /*      Cyclone-timer: 
2  *              This code implements timer_ops for the cyclone counter found
3  *              on IBM x440, x360, and other Summit based systems.
4  *
5  *      Copyright (C) 2002 IBM, John Stultz (johnstul@us.ibm.com)
6  */
7
8
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>
15
16 #include <asm/timer.h>
17 #include <asm/io.h>
18 #include <asm/pgtable.h>
19 #include <asm/fixmap.h>
20
21 extern spinlock_t i8253_lock;
22
23 /* Number of usecs that the last interrupt was delayed */
24 static int delay_at_last_interrupt;
25
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 */
32 int use_cyclone = 0;
33
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;
39
40 /* helper macro to atomically read both cyclone counter registers */
41 #define read_cyclone_counter(low,high) \
42         do{ \
43                 high = cyclone_timer[1]; low = cyclone_timer[0]; \
44         } while (high != cyclone_timer[1]);
45
46
47 static void mark_offset_cyclone(void)
48 {
49         unsigned long lost, delay;
50         unsigned long delta = last_cyclone_low;
51         int count;
52         unsigned long long this_offset, last_offset;
53
54         write_seqlock(&monotonic_lock);
55         last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
56         
57         spin_lock(&i8253_lock);
58         read_cyclone_counter(last_cyclone_low,last_cyclone_high);
59
60         /* read values for delay_at_last_interrupt */
61         outb_p(0x00, 0x43);     /* latch the count ASAP */
62
63         count = inb_p(0x40);    /* read the latched count */
64         count |= inb(0x40) << 8;
65         spin_unlock(&i8253_lock);
66
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);
73         if (lost >= 2)
74                 jiffies_64 += lost-1;
75         
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);
80
81         /* calculate delay_at_last_interrupt */
82         count = ((LATCH-1) - count) * TICK_SIZE;
83         delay_at_last_interrupt = (count + LATCH/2) / LATCH;
84
85
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)
89          */
90         if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
91                 jiffies_64++;
92 }
93
94 static unsigned long get_offset_cyclone(void)
95 {
96         u32 offset;
97
98         if(!cyclone_timer)
99                 return delay_at_last_interrupt;
100
101         /* Read the cyclone timer */
102         offset = cyclone_timer[0];
103
104         /* .. relative to previous jiffy */
105         offset = offset - last_cyclone_low;
106
107         /* convert cyclone ticks to microseconds */     
108         /* XXX slow, can we speed this up? */
109         offset = offset/(CYCLONE_TIMER_FREQ/1000000);
110
111         /* our adjusted time offset in microseconds */
112         return delay_at_last_interrupt + offset;
113 }
114
115 static unsigned long long monotonic_clock_cyclone(void)
116 {
117         u32 now_low, now_high;
118         unsigned long long last_offset, this_offset, base;
119         unsigned long long ret;
120         unsigned seq;
121
122         /* atomically read monotonic base & last_offset */
123         do {
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));
128
129
130         /* Read the cyclone counter */
131         read_cyclone_counter(now_low,now_high);
132         this_offset = ((unsigned long long)now_high<<32)|now_low;
133
134         /* convert to nanoseconds */
135         ret = base + ((this_offset - last_offset)&CYCLONE_TIMER_MASK);
136         return ret * (1000000000 / CYCLONE_TIMER_FREQ);
137 }
138
139 static int __init init_cyclone(char* override)
140 {
141         u32* reg;       
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 */
145         int i;
146         
147         /* check clock override */
148         if (override[0] && strncmp(override,"cyclone",7))
149                         return -ENODEV;
150
151         /*make sure we're on a summit box*/
152         if(!use_cyclone) return -ENODEV; 
153         
154         printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
155
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);
161         if(!reg){
162                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
163                 return -ENODEV;
164         }
165         base = *reg;    
166         if(!base){
167                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
168                 return -ENODEV;
169         }
170         
171         /* setup PMCC */
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);
176         if(!reg){
177                 printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
178                 return -ENODEV;
179         }
180         reg[0] = 0x00000001;
181
182         /* setup MPCS */
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);
187         if(!reg){
188                 printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
189                 return -ENODEV;
190         }
191         reg[0] = 0x00000001;
192
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);
198         if(!cyclone_timer){
199                 printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
200                 return -ENODEV;
201         }
202
203         /*quick test to make sure its ticking*/
204         for(i=0; i<3; i++){
205                 u32 old = cyclone_timer[0];
206                 int stall = 100;
207                 while(stall--) barrier();
208                 if(cyclone_timer[0] == old){
209                         printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
210                         cyclone_timer = 0;
211                         return -ENODEV;
212                 }
213         }
214
215         init_cpu_khz();
216
217         /* Everything looks good! */
218         return 0;
219 }
220
221
222 static void delay_cyclone(unsigned long loops)
223 {
224         unsigned long bclock, now;
225         if(!cyclone_timer)
226                 return;
227         bclock = cyclone_timer[0];
228         do {
229                 rep_nop();
230                 now = cyclone_timer[0];
231         } while ((now-bclock) < loops);
232 }
233 /************************************************************/
234
235 /* cyclone timer_opts struct */
236 struct timer_opts timer_cyclone = {
237         .name = "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,
243 };