1#include <linux/module.h>
2#include <linux/smp.h>
3#include <linux/time.h>
4#include <linux/errno.h>
5#include <linux/timex.h>
6#include <linux/clocksource.h>
7#include <asm/io.h>
8
9/* IBM Summit (EXA) Cyclone counter code*/
10#define CYCLONE_CBAR_ADDR 0xFEB00CD0
11#define CYCLONE_PMCC_OFFSET 0x51A0
12#define CYCLONE_MPMC_OFFSET 0x51D0
13#define CYCLONE_MPCS_OFFSET 0x51A8
14#define CYCLONE_TIMER_FREQ 100000000
15
16int use_cyclone;
17void __init cyclone_setup(void)
18{
19	use_cyclone = 1;
20}
21
22static void __iomem *cyclone_mc;
23
24static cycle_t read_cyclone(struct clocksource *cs)
25{
26	return (cycle_t)readq((void __iomem *)cyclone_mc);
27}
28
29static struct clocksource clocksource_cyclone = {
30        .name           = "cyclone",
31        .rating         = 300,
32        .read           = read_cyclone,
33        .mask           = (1LL << 40) - 1,
34        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
35};
36
37int __init init_cyclone_clock(void)
38{
39	u64 __iomem *reg;
40	u64 base;	/* saved cyclone base address */
41	u64 offset;	/* offset from pageaddr to cyclone_timer register */
42	int i;
43	u32 __iomem *cyclone_timer;	/* Cyclone MPMC0 register */
44
45	if (!use_cyclone)
46		return 0;
47
48	printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
49
50	/* find base address */
51	offset = (CYCLONE_CBAR_ADDR);
52	reg = ioremap_nocache(offset, sizeof(u64));
53	if(!reg){
54		printk(KERN_ERR "Summit chipset: Could not find valid CBAR"
55				" register.\n");
56		use_cyclone = 0;
57		return -ENODEV;
58	}
59	base = readq(reg);
60	iounmap(reg);
61	if(!base){
62		printk(KERN_ERR "Summit chipset: Could not find valid CBAR"
63				" value.\n");
64		use_cyclone = 0;
65		return -ENODEV;
66	}
67
68	/* setup PMCC */
69	offset = (base + CYCLONE_PMCC_OFFSET);
70	reg = ioremap_nocache(offset, sizeof(u64));
71	if(!reg){
72		printk(KERN_ERR "Summit chipset: Could not find valid PMCC"
73				" register.\n");
74		use_cyclone = 0;
75		return -ENODEV;
76	}
77	writel(0x00000001,reg);
78	iounmap(reg);
79
80	/* setup MPCS */
81	offset = (base + CYCLONE_MPCS_OFFSET);
82	reg = ioremap_nocache(offset, sizeof(u64));
83	if(!reg){
84		printk(KERN_ERR "Summit chipset: Could not find valid MPCS"
85				" register.\n");
86		use_cyclone = 0;
87		return -ENODEV;
88	}
89	writel(0x00000001,reg);
90	iounmap(reg);
91
92	/* map in cyclone_timer */
93	offset = (base + CYCLONE_MPMC_OFFSET);
94	cyclone_timer = ioremap_nocache(offset, sizeof(u32));
95	if(!cyclone_timer){
96		printk(KERN_ERR "Summit chipset: Could not find valid MPMC"
97				" register.\n");
98		use_cyclone = 0;
99		return -ENODEV;
100	}
101
102	/*quick test to make sure its ticking*/
103	for(i=0; i<3; i++){
104		u32 old = readl(cyclone_timer);
105		int stall = 100;
106		while(stall--) barrier();
107		if(readl(cyclone_timer) == old){
108			printk(KERN_ERR "Summit chipset: Counter not counting!"
109					" DISABLED\n");
110			iounmap(cyclone_timer);
111			cyclone_timer = NULL;
112			use_cyclone = 0;
113			return -ENODEV;
114		}
115	}
116	/* initialize last tick */
117	cyclone_mc = cyclone_timer;
118	clocksource_cyclone.archdata.fsys_mmio = cyclone_timer;
119	clocksource_register_hz(&clocksource_cyclone, CYCLONE_TIMER_FREQ);
120
121	return 0;
122}
123
124__initcall(init_cyclone_clock);
125