ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / kernel / cyclone.c
1 #include <linux/smp.h>
2 #include <linux/time.h>
3 #include <linux/errno.h>
4
5 /* IBM Summit (EXA) Cyclone counter code*/
6 #define CYCLONE_CBAR_ADDR 0xFEB00CD0
7 #define CYCLONE_PMCC_OFFSET 0x51A0
8 #define CYCLONE_MPMC_OFFSET 0x51D0
9 #define CYCLONE_MPCS_OFFSET 0x51A8
10 #define CYCLONE_TIMER_FREQ 100000000
11
12 int use_cyclone;
13 int __init cyclone_setup(char *str)
14 {
15         use_cyclone = 1;
16         return 1;
17 }
18
19 static u32* volatile cyclone_timer;     /* Cyclone MPMC0 register */
20 static u32 last_update_cyclone;
21
22 static unsigned long offset_base;
23
24 static unsigned long get_offset_cyclone(void)
25 {
26         u32 now;
27         unsigned long offset;
28
29         /* Read the cyclone timer */
30         now = readl(cyclone_timer);
31         /* .. relative to previous update*/
32         offset = now - last_update_cyclone;
33
34         /* convert cyclone ticks to nanoseconds */
35         offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ;
36
37         /* our adjusted time in nanoseconds */
38         return offset_base + offset;
39 }
40
41 static void update_cyclone(long delta_nsec)
42 {
43         u32 now;
44         unsigned long offset;
45
46         /* Read the cyclone timer */
47         now = readl(cyclone_timer);
48         /* .. relative to previous update*/
49         offset = now - last_update_cyclone;
50
51         /* convert cyclone ticks to nanoseconds */
52         offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ;
53
54         offset += offset_base;
55
56         /* Be careful about signed/unsigned comparisons here: */
57         if (delta_nsec < 0 || (unsigned long) delta_nsec < offset)
58                 offset_base = offset - delta_nsec;
59         else
60                 offset_base = 0;
61
62         last_update_cyclone = now;
63 }
64
65 static void reset_cyclone(void)
66 {
67         offset_base = 0;
68         last_update_cyclone = readl(cyclone_timer);
69 }
70
71 struct time_interpolator cyclone_interpolator = {
72         .get_offset =   get_offset_cyclone,
73         .update =       update_cyclone,
74         .reset =        reset_cyclone,
75         .frequency =    CYCLONE_TIMER_FREQ,
76         .drift =        -100,
77 };
78
79 int __init init_cyclone_clock(void)
80 {
81         u64* reg;
82         u64 base;       /* saved cyclone base address */
83         u64 offset;     /* offset from pageaddr to cyclone_timer register */
84         int i;
85
86         if (!use_cyclone)
87                 return -ENODEV;
88
89         printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
90
91         /* find base address */
92         offset = (CYCLONE_CBAR_ADDR);
93         reg = (u64*)ioremap_nocache(offset, sizeof(u64));
94         if(!reg){
95                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
96                 use_cyclone = 0;
97                 return -ENODEV;
98         }
99         base = readq(reg);
100         if(!base){
101                 printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
102                 use_cyclone = 0;
103                 return -ENODEV;
104         }
105         iounmap(reg);
106
107         /* setup PMCC */
108         offset = (base + CYCLONE_PMCC_OFFSET);
109         reg = (u64*)ioremap_nocache(offset, sizeof(u64));
110         if(!reg){
111                 printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
112                 use_cyclone = 0;
113                 return -ENODEV;
114         }
115         writel(0x00000001,reg);
116         iounmap(reg);
117
118         /* setup MPCS */
119         offset = (base + CYCLONE_MPCS_OFFSET);
120         reg = (u64*)ioremap_nocache(offset, sizeof(u64));
121         if(!reg){
122                 printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
123                 use_cyclone = 0;
124                 return -ENODEV;
125         }
126         writel(0x00000001,reg);
127         iounmap(reg);
128
129         /* map in cyclone_timer */
130         offset = (base + CYCLONE_MPMC_OFFSET);
131         cyclone_timer = (u32*)ioremap_nocache(offset, sizeof(u32));
132         if(!cyclone_timer){
133                 printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
134                 use_cyclone = 0;
135                 return -ENODEV;
136         }
137
138         /*quick test to make sure its ticking*/
139         for(i=0; i<3; i++){
140                 u32 old = readl(cyclone_timer);
141                 int stall = 100;
142                 while(stall--) barrier();
143                 if(readl(cyclone_timer) == old){
144                         printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
145                         iounmap(cyclone_timer);
146                         cyclone_timer = 0;
147                         use_cyclone = 0;
148                         return -ENODEV;
149                 }
150         }
151         /* initialize last tick */
152         last_update_cyclone = readl(cyclone_timer);
153         register_time_interpolator(&cyclone_interpolator);
154
155         return 0;
156 }
157
158 __initcall(init_cyclone_clock);