diff options
Diffstat (limited to 'arch/ia64/kernel/cyclone.c')
-rw-r--r-- | arch/ia64/kernel/cyclone.c | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/arch/ia64/kernel/cyclone.c b/arch/ia64/kernel/cyclone.c new file mode 100644 index 000000000000..768c7e46957c --- /dev/null +++ b/arch/ia64/kernel/cyclone.c | |||
@@ -0,0 +1,109 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/smp.h> | ||
3 | #include <linux/time.h> | ||
4 | #include <linux/errno.h> | ||
5 | #include <asm/io.h> | ||
6 | |||
7 | /* IBM Summit (EXA) Cyclone counter code*/ | ||
8 | #define CYCLONE_CBAR_ADDR 0xFEB00CD0 | ||
9 | #define CYCLONE_PMCC_OFFSET 0x51A0 | ||
10 | #define CYCLONE_MPMC_OFFSET 0x51D0 | ||
11 | #define CYCLONE_MPCS_OFFSET 0x51A8 | ||
12 | #define CYCLONE_TIMER_FREQ 100000000 | ||
13 | |||
14 | int use_cyclone; | ||
15 | void __init cyclone_setup(void) | ||
16 | { | ||
17 | use_cyclone = 1; | ||
18 | } | ||
19 | |||
20 | |||
21 | struct time_interpolator cyclone_interpolator = { | ||
22 | .source = TIME_SOURCE_MMIO64, | ||
23 | .shift = 16, | ||
24 | .frequency = CYCLONE_TIMER_FREQ, | ||
25 | .drift = -100, | ||
26 | .mask = (1LL << 40) - 1 | ||
27 | }; | ||
28 | |||
29 | int __init init_cyclone_clock(void) | ||
30 | { | ||
31 | u64* reg; | ||
32 | u64 base; /* saved cyclone base address */ | ||
33 | u64 offset; /* offset from pageaddr to cyclone_timer register */ | ||
34 | int i; | ||
35 | u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ | ||
36 | |||
37 | if (!use_cyclone) | ||
38 | return -ENODEV; | ||
39 | |||
40 | printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n"); | ||
41 | |||
42 | /* find base address */ | ||
43 | offset = (CYCLONE_CBAR_ADDR); | ||
44 | reg = (u64*)ioremap_nocache(offset, sizeof(u64)); | ||
45 | if(!reg){ | ||
46 | printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n"); | ||
47 | use_cyclone = 0; | ||
48 | return -ENODEV; | ||
49 | } | ||
50 | base = readq(reg); | ||
51 | if(!base){ | ||
52 | printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n"); | ||
53 | use_cyclone = 0; | ||
54 | return -ENODEV; | ||
55 | } | ||
56 | iounmap(reg); | ||
57 | |||
58 | /* setup PMCC */ | ||
59 | offset = (base + CYCLONE_PMCC_OFFSET); | ||
60 | reg = (u64*)ioremap_nocache(offset, sizeof(u64)); | ||
61 | if(!reg){ | ||
62 | printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n"); | ||
63 | use_cyclone = 0; | ||
64 | return -ENODEV; | ||
65 | } | ||
66 | writel(0x00000001,reg); | ||
67 | iounmap(reg); | ||
68 | |||
69 | /* setup MPCS */ | ||
70 | offset = (base + CYCLONE_MPCS_OFFSET); | ||
71 | reg = (u64*)ioremap_nocache(offset, sizeof(u64)); | ||
72 | if(!reg){ | ||
73 | printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n"); | ||
74 | use_cyclone = 0; | ||
75 | return -ENODEV; | ||
76 | } | ||
77 | writel(0x00000001,reg); | ||
78 | iounmap(reg); | ||
79 | |||
80 | /* map in cyclone_timer */ | ||
81 | offset = (base + CYCLONE_MPMC_OFFSET); | ||
82 | cyclone_timer = (u32*)ioremap_nocache(offset, sizeof(u32)); | ||
83 | if(!cyclone_timer){ | ||
84 | printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n"); | ||
85 | use_cyclone = 0; | ||
86 | return -ENODEV; | ||
87 | } | ||
88 | |||
89 | /*quick test to make sure its ticking*/ | ||
90 | for(i=0; i<3; i++){ | ||
91 | u32 old = readl(cyclone_timer); | ||
92 | int stall = 100; | ||
93 | while(stall--) barrier(); | ||
94 | if(readl(cyclone_timer) == old){ | ||
95 | printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n"); | ||
96 | iounmap(cyclone_timer); | ||
97 | cyclone_timer = 0; | ||
98 | use_cyclone = 0; | ||
99 | return -ENODEV; | ||
100 | } | ||
101 | } | ||
102 | /* initialize last tick */ | ||
103 | cyclone_interpolator.addr = cyclone_timer; | ||
104 | register_time_interpolator(&cyclone_interpolator); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | __initcall(init_cyclone_clock); | ||