Commit | Line | Data |
---|---|---|
59d3a193 PZ |
1 | /* |
2 | * Copyright (C) 2001-2006 Storlink, Corp. | |
3 | * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | */ | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/irq.h> | |
12 | #include <linux/io.h> | |
13 | #include <mach/hardware.h> | |
14 | #include <mach/global_reg.h> | |
15 | #include <asm/mach/time.h> | |
f3372c01 LW |
16 | #include <linux/clockchips.h> |
17 | #include <linux/clocksource.h> | |
d330615b | 18 | #include <linux/sched_clock.h> |
59d3a193 PZ |
19 | |
20 | /* | |
21 | * Register definitions for the timers | |
22 | */ | |
570ceed4 HUK |
23 | |
24 | #define TIMER1_BASE GEMINI_TIMER_BASE | |
25 | #define TIMER2_BASE (GEMINI_TIMER_BASE + 0x10) | |
26 | #define TIMER3_BASE (GEMINI_TIMER_BASE + 0x20) | |
27 | ||
28 | #define TIMER_COUNT(BASE) (IO_ADDRESS(BASE) + 0x00) | |
29 | #define TIMER_LOAD(BASE) (IO_ADDRESS(BASE) + 0x04) | |
30 | #define TIMER_MATCH1(BASE) (IO_ADDRESS(BASE) + 0x08) | |
31 | #define TIMER_MATCH2(BASE) (IO_ADDRESS(BASE) + 0x0C) | |
32 | #define TIMER_CR (IO_ADDRESS(GEMINI_TIMER_BASE) + 0x30) | |
33 | #define TIMER_INTR_STATE (IO_ADDRESS(GEMINI_TIMER_BASE) + 0x34) | |
34 | #define TIMER_INTR_MASK (IO_ADDRESS(GEMINI_TIMER_BASE) + 0x38) | |
35 | ||
36 | #define TIMER_1_CR_ENABLE (1 << 0) | |
37 | #define TIMER_1_CR_CLOCK (1 << 1) | |
38 | #define TIMER_1_CR_INT (1 << 2) | |
39 | #define TIMER_2_CR_ENABLE (1 << 3) | |
40 | #define TIMER_2_CR_CLOCK (1 << 4) | |
41 | #define TIMER_2_CR_INT (1 << 5) | |
42 | #define TIMER_3_CR_ENABLE (1 << 6) | |
43 | #define TIMER_3_CR_CLOCK (1 << 7) | |
44 | #define TIMER_3_CR_INT (1 << 8) | |
45 | #define TIMER_1_CR_UPDOWN (1 << 9) | |
46 | #define TIMER_2_CR_UPDOWN (1 << 10) | |
47 | #define TIMER_3_CR_UPDOWN (1 << 11) | |
48 | #define TIMER_DEFAULT_FLAGS (TIMER_1_CR_UPDOWN | \ | |
49 | TIMER_3_CR_ENABLE | \ | |
50 | TIMER_3_CR_UPDOWN) | |
51 | ||
52 | #define TIMER_1_INT_MATCH1 (1 << 0) | |
53 | #define TIMER_1_INT_MATCH2 (1 << 1) | |
54 | #define TIMER_1_INT_OVERFLOW (1 << 2) | |
55 | #define TIMER_2_INT_MATCH1 (1 << 3) | |
56 | #define TIMER_2_INT_MATCH2 (1 << 4) | |
57 | #define TIMER_2_INT_OVERFLOW (1 << 5) | |
58 | #define TIMER_3_INT_MATCH1 (1 << 6) | |
59 | #define TIMER_3_INT_MATCH2 (1 << 7) | |
60 | #define TIMER_3_INT_OVERFLOW (1 << 8) | |
61 | #define TIMER_INT_ALL_MASK 0x1ff | |
62 | ||
59d3a193 | 63 | |
f3372c01 LW |
64 | static unsigned int tick_rate; |
65 | ||
d330615b HUK |
66 | static u64 notrace gemini_read_sched_clock(void) |
67 | { | |
68 | return readl(TIMER_COUNT(TIMER3_BASE)); | |
69 | } | |
70 | ||
f3372c01 LW |
71 | static int gemini_timer_set_next_event(unsigned long cycles, |
72 | struct clock_event_device *evt) | |
73 | { | |
74 | u32 cr; | |
75 | ||
5dc90739 HUK |
76 | /* Setup the match register */ |
77 | cr = readl(TIMER_COUNT(TIMER1_BASE)); | |
78 | writel(cr + cycles, TIMER_MATCH1(TIMER1_BASE)); | |
79 | if (readl(TIMER_COUNT(TIMER1_BASE)) - cr > cycles) | |
80 | return -ETIME; | |
f3372c01 LW |
81 | |
82 | return 0; | |
83 | } | |
84 | ||
e2efda24 VK |
85 | static int gemini_timer_shutdown(struct clock_event_device *evt) |
86 | { | |
87 | u32 cr; | |
88 | ||
89 | /* | |
90 | * Disable also for oneshot: the set_next() call will arm the timer | |
91 | * instead. | |
92 | */ | |
5dc90739 | 93 | /* Stop timer and interrupt. */ |
570ceed4 | 94 | cr = readl(TIMER_CR); |
5dc90739 | 95 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); |
570ceed4 | 96 | writel(cr, TIMER_CR); |
5dc90739 HUK |
97 | |
98 | /* Setup counter start from 0 */ | |
99 | writel(0, TIMER_COUNT(TIMER1_BASE)); | |
100 | writel(0, TIMER_LOAD(TIMER1_BASE)); | |
101 | ||
102 | /* enable interrupt */ | |
103 | cr = readl(TIMER_INTR_MASK); | |
104 | cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2); | |
105 | cr |= TIMER_1_INT_MATCH1; | |
106 | writel(cr, TIMER_INTR_MASK); | |
107 | ||
108 | /* start the timer */ | |
109 | cr = readl(TIMER_CR); | |
110 | cr |= TIMER_1_CR_ENABLE; | |
111 | writel(cr, TIMER_CR); | |
112 | ||
e2efda24 VK |
113 | return 0; |
114 | } | |
115 | ||
116 | static int gemini_timer_set_periodic(struct clock_event_device *evt) | |
f3372c01 LW |
117 | { |
118 | u32 period = DIV_ROUND_CLOSEST(tick_rate, HZ); | |
119 | u32 cr; | |
120 | ||
5dc90739 HUK |
121 | /* Stop timer and interrupt */ |
122 | cr = readl(TIMER_CR); | |
123 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); | |
124 | writel(cr, TIMER_CR); | |
125 | ||
126 | /* Setup timer to fire at 1/HT intervals. */ | |
127 | cr = 0xffffffff - (period - 1); | |
128 | writel(cr, TIMER_COUNT(TIMER1_BASE)); | |
129 | writel(cr, TIMER_LOAD(TIMER1_BASE)); | |
130 | ||
131 | /* enable interrupt on overflow */ | |
132 | cr = readl(TIMER_INTR_MASK); | |
133 | cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2); | |
134 | cr |= TIMER_1_INT_OVERFLOW; | |
135 | writel(cr, TIMER_INTR_MASK); | |
136 | ||
e2efda24 | 137 | /* Start the timer */ |
570ceed4 | 138 | cr = readl(TIMER_CR); |
5dc90739 HUK |
139 | cr |= TIMER_1_CR_ENABLE; |
140 | cr |= TIMER_1_CR_INT; | |
570ceed4 | 141 | writel(cr, TIMER_CR); |
5dc90739 | 142 | |
e2efda24 | 143 | return 0; |
f3372c01 LW |
144 | } |
145 | ||
5dc90739 | 146 | /* Use TIMER1 as clock event */ |
f3372c01 | 147 | static struct clock_event_device gemini_clockevent = { |
5dc90739 | 148 | .name = "TIMER1", |
e2efda24 VK |
149 | /* Reasonably fast and accurate clock event */ |
150 | .rating = 300, | |
5dc90739 | 151 | .shift = 32, |
e2efda24 VK |
152 | .features = CLOCK_EVT_FEAT_PERIODIC | |
153 | CLOCK_EVT_FEAT_ONESHOT, | |
154 | .set_next_event = gemini_timer_set_next_event, | |
155 | .set_state_shutdown = gemini_timer_shutdown, | |
156 | .set_state_periodic = gemini_timer_set_periodic, | |
157 | .set_state_oneshot = gemini_timer_shutdown, | |
158 | .tick_resume = gemini_timer_shutdown, | |
f3372c01 LW |
159 | }; |
160 | ||
59d3a193 PZ |
161 | /* |
162 | * IRQ handler for the timer | |
163 | */ | |
164 | static irqreturn_t gemini_timer_interrupt(int irq, void *dev_id) | |
165 | { | |
f3372c01 | 166 | struct clock_event_device *evt = &gemini_clockevent; |
59d3a193 | 167 | |
f3372c01 | 168 | evt->event_handler(evt); |
59d3a193 PZ |
169 | return IRQ_HANDLED; |
170 | } | |
171 | ||
172 | static struct irqaction gemini_timer_irq = { | |
173 | .name = "Gemini Timer Tick", | |
f3372c01 | 174 | .flags = IRQF_TIMER, |
59d3a193 PZ |
175 | .handler = gemini_timer_interrupt, |
176 | }; | |
177 | ||
178 | /* | |
179 | * Set up timer interrupt, and return the current time in seconds. | |
180 | */ | |
181 | void __init gemini_timer_init(void) | |
182 | { | |
f3372c01 | 183 | u32 reg_v; |
59d3a193 | 184 | |
f3372c01 | 185 | reg_v = readl(IO_ADDRESS(GEMINI_GLOBAL_BASE + GLOBAL_STATUS)); |
59d3a193 PZ |
186 | tick_rate = REG_TO_AHB_SPEED(reg_v) * 1000000; |
187 | ||
188 | printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000); | |
189 | ||
190 | tick_rate /= 6; /* APB bus run AHB*(1/6) */ | |
191 | ||
192 | switch(reg_v & CPU_AHB_RATIO_MASK) { | |
193 | case CPU_AHB_1_1: | |
194 | printk(KERN_CONT "(1/1)\n"); | |
195 | break; | |
196 | case CPU_AHB_3_2: | |
197 | printk(KERN_CONT "(3/2)\n"); | |
198 | break; | |
199 | case CPU_AHB_24_13: | |
200 | printk(KERN_CONT "(24/13)\n"); | |
201 | break; | |
202 | case CPU_AHB_2_1: | |
203 | printk(KERN_CONT "(2/1)\n"); | |
204 | break; | |
205 | } | |
206 | ||
207 | /* | |
5dc90739 | 208 | * Reset the interrupt mask and status |
59d3a193 | 209 | */ |
5dc90739 HUK |
210 | writel(TIMER_INT_ALL_MASK, TIMER_INTR_MASK); |
211 | writel(0, TIMER_INTR_STATE); | |
212 | writel(TIMER_DEFAULT_FLAGS, TIMER_CR); | |
213 | ||
214 | /* | |
d330615b HUK |
215 | * Setup free-running clocksource timer (interrupts |
216 | * disabled.) | |
5dc90739 | 217 | */ |
d330615b HUK |
218 | writel(0, TIMER_COUNT(TIMER3_BASE)); |
219 | writel(0, TIMER_LOAD(TIMER3_BASE)); | |
220 | writel(0, TIMER_MATCH1(TIMER3_BASE)); | |
221 | writel(0, TIMER_MATCH2(TIMER3_BASE)); | |
222 | clocksource_mmio_init(TIMER_COUNT(TIMER3_BASE), | |
223 | "gemini_clocksource", tick_rate, | |
224 | 300, 32, clocksource_mmio_readl_up); | |
225 | sched_clock_register(gemini_read_sched_clock, 32, tick_rate); | |
226 | ||
227 | /* | |
228 | * Setup clockevent timer (interrupt-driven.) | |
229 | */ | |
5dc90739 HUK |
230 | writel(0, TIMER_COUNT(TIMER1_BASE)); |
231 | writel(0, TIMER_LOAD(TIMER1_BASE)); | |
232 | writel(0, TIMER_MATCH1(TIMER1_BASE)); | |
233 | writel(0, TIMER_MATCH2(TIMER1_BASE)); | |
234 | setup_irq(IRQ_TIMER1, &gemini_timer_irq); | |
235 | gemini_clockevent.cpumask = cpumask_of(0); | |
f3372c01 LW |
236 | clockevents_config_and_register(&gemini_clockevent, tick_rate, |
237 | 1, 0xffffffff); | |
5dc90739 | 238 | |
59d3a193 | 239 | } |