]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/i386/kernel/timers/timer_cyclone.c
Merge rsync://www.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6-omap-h63xx.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 #include <asm/i8253.h>
21
22 #include "io_ports.h"
23
24 /* Number of usecs that the last interrupt was delayed */
25 static int delay_at_last_interrupt;
26
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 */
33 int use_cyclone = 0;
34
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;
40
41 /* helper macro to atomically read both cyclone counter registers */
42 #define read_cyclone_counter(low,high) \
43         do{ \
44                 high = cyclone_timer[1]; low = cyclone_timer[0]; \
45         } while (high != cyclone_timer[1]);
46
47
48 static void mark_offset_cyclone(void)
49 {
50         unsigned long lost, delay;
51         unsigned long delta = last_cyclone_low;
52         int count;
53         unsigned long long this_offset, last_offset;
54
55         write_seqlock(&monotonic_lock);
56         last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
57         
58         spin_lock(&i8253_lock);
59         read_cyclone_counter(last_cyclone_low,last_cyclone_high);
60
61         /* read values for delay_at_last_interrupt */
62         outb_p(0x00, 0x43);     /* latch the count ASAP */
63
64         count = inb_p(0x40);    /* read the latched count */
65         count |= inb(0x40) << 8;
66
67         /*
68          * VIA686a test code... reset the latch if count > max + 1
69          * from timer_pit.c - cjb
70          */
71         if (count > LATCH) {
72                 outb_p(0x34, PIT_MODE);
73                 outb_p(LATCH & 0xff, PIT_CH0);
74                 outb(LATCH >> 8, PIT_CH0);
75                 count = LATCH - 1;
76         }
77         spin_unlock(&i8253_lock);
78
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);
85         if (lost >= 2)
86                 jiffies_64 += lost-1;
87         
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);
92
93         /* calculate delay_at_last_interrupt */
94         count = ((LATCH-1) - count) * TICK_SIZE;
95         delay_at_last_interrupt = (count + LATCH/2) / LATCH;
96
97
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)
101          */
102         if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
103                 jiffies_64++;
104 }
105
106 static unsigned long get_offset_cyclone(void)
107 {
108         u32 offset;
109
110         if(!cyclone_timer)
111                 return delay_at_last_interrupt;
112
113         /* Read the cyclone timer */
114         offset = cyclone_timer[0];
115
116         /* .. relative to previous jiffy */
117         offset = offset - last_cyclone_low;
118
119         /* convert cyclone ticks to microseconds */     
120         /* XXX slow, can we speed this up? */
121         offset = offset/(CYCLONE_TIMER_FREQ/1000000);
122
123         /* our adjusted time offset in microseconds */
124         return delay_at_last_interrupt + offset;
125 }
126
127 static unsigned long long monotonic_clock_cyclone(void)
128 {
129         u32 now_low, now_high;
130         unsigned long long last_offset, this_offset, base;
131         unsigned long long ret;
132         unsigned seq;
133
134         /* atomically read monotonic base & last_offset */
135         do {
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));
140
141
142         /* Read the cyclone counter */
143         read_cyclone_counter(now_low,now_high);
144         this_offset = ((unsigned long long)now_high<<32)|now_low;
145
146         /* convert to nanoseconds */
147         ret = base + ((this_offset - last_offset)&CYCLONE_TIMER_MASK);
148         return ret * (1000000000 / CYCLONE_TIMER_FREQ);
149 }
150
151 static int __init init_cyclone(char* override)
152 {
153         u32* reg;       
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 */
157         int i;
158         
159         /* check clock override */
160         if (override[0] && strncmp(override,"cyclone",7))
161                         return -ENODEV;
162
163         /*make sure we're on a summit box*/
164         if(!use_cyclone) return -ENODEV; 
165         
166         printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
167
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);
173         if(!reg){
174                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
175                 return -ENODEV;
176         }
177         base = *reg;    
178         if(!base){
179                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
180                 return -ENODEV;
181         }
182         
183         /* setup PMCC */
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);
188         if(!reg){
189                 printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
190                 return -ENODEV;
191         }
192         reg[0] = 0x00000001;
193
194         /* setup MPCS */
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);
199         if(!reg){
200                 printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
201                 return -ENODEV;
202         }
203         reg[0] = 0x00000001;
204
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);
210         if(!cyclone_timer){
211                 printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
212                 return -ENODEV;
213         }
214
215         /*quick test to make sure its ticking*/
216         for(i=0; i<3; i++){
217                 u32 old = cyclone_timer[0];
218                 int stall = 100;
219                 while(stall--) barrier();
220                 if(cyclone_timer[0] == old){
221                         printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
222                         cyclone_timer = 0;
223                         return -ENODEV;
224                 }
225         }
226
227         init_cpu_khz();
228
229         /* Everything looks good! */
230         return 0;
231 }
232
233
234 static void delay_cyclone(unsigned long loops)
235 {
236         unsigned long bclock, now;
237         if(!cyclone_timer)
238                 return;
239         bclock = cyclone_timer[0];
240         do {
241                 rep_nop();
242                 now = cyclone_timer[0];
243         } while ((now-bclock) < loops);
244 }
245 /************************************************************/
246
247 /* cyclone timer_opts struct */
248 static struct timer_opts timer_cyclone = {
249         .name = "cyclone",
250         .mark_offset = mark_offset_cyclone, 
251         .get_offset = get_offset_cyclone,
252         .monotonic_clock =      monotonic_clock_cyclone,
253         .delay = delay_cyclone,
254 };
255
256 struct init_timer_opts __initdata timer_cyclone_init = {
257         .init = init_cyclone,
258         .opts = &timer_cyclone,
259 };