Commit | Line | Data |
---|---|---|
8b5f79f9 VM |
1 | /* |
2 | * linux/arch/kernel/time-ts.c | |
3 | * | |
4 | * Based on arm clockevents implementation and old bfin time tick. | |
5 | * | |
6 | * Copyright(C) 2008, GeoTechnologies, Vitja Makarov | |
7 | * | |
8 | * This code is licenced under the GPL version 2. For details see | |
9 | * kernel-base/COPYING. | |
10 | */ | |
11 | #include <linux/module.h> | |
12 | #include <linux/profile.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/time.h> | |
764cb81c | 15 | #include <linux/timex.h> |
8b5f79f9 VM |
16 | #include <linux/irq.h> |
17 | #include <linux/clocksource.h> | |
18 | #include <linux/clockchips.h> | |
e6c91b64 | 19 | #include <linux/cpufreq.h> |
8b5f79f9 VM |
20 | |
21 | #include <asm/blackfin.h> | |
e6c91b64 | 22 | #include <asm/time.h> |
1fa9be72 | 23 | #include <asm/gptimers.h> |
8b5f79f9 | 24 | |
1fa9be72 | 25 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) |
8b5f79f9 | 26 | |
e6c91b64 MH |
27 | /* Accelerators for sched_clock() |
28 | * convert from cycles(64bits) => nanoseconds (64bits) | |
29 | * basic equation: | |
30 | * ns = cycles / (freq / ns_per_sec) | |
31 | * ns = cycles * (ns_per_sec / freq) | |
32 | * ns = cycles * (10^9 / (cpu_khz * 10^3)) | |
33 | * ns = cycles * (10^6 / cpu_khz) | |
34 | * | |
35 | * Then we use scaling math (suggested by george@mvista.com) to get: | |
36 | * ns = cycles * (10^6 * SC / cpu_khz) / SC | |
37 | * ns = cycles * cyc2ns_scale / SC | |
38 | * | |
39 | * And since SC is a constant power of two, we can convert the div | |
40 | * into a shift. | |
41 | * | |
42 | * We can use khz divisor instead of mhz to keep a better precision, since | |
43 | * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits. | |
44 | * (mathieu.desnoyers@polymtl.ca) | |
45 | * | |
46 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" | |
47 | */ | |
48 | ||
8b5f79f9 VM |
49 | static unsigned long cyc2ns_scale; |
50 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | |
51 | ||
52 | static inline void set_cyc2ns_scale(unsigned long cpu_khz) | |
53 | { | |
54 | cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR) / cpu_khz; | |
55 | } | |
56 | ||
57 | static inline unsigned long long cycles_2_ns(cycle_t cyc) | |
58 | { | |
59 | return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; | |
60 | } | |
61 | ||
1fa9be72 | 62 | static cycle_t bfin_read_cycles(struct clocksource *cs) |
8b5f79f9 | 63 | { |
1bfb4b21 | 64 | return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod); |
8b5f79f9 VM |
65 | } |
66 | ||
1fa9be72 GY |
67 | static struct clocksource bfin_cs_cycles = { |
68 | .name = "bfin_cs_cycles", | |
8b5f79f9 | 69 | .rating = 350, |
1fa9be72 | 70 | .read = bfin_read_cycles, |
8b5f79f9 VM |
71 | .mask = CLOCKSOURCE_MASK(64), |
72 | .shift = 22, | |
73 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
74 | }; | |
75 | ||
8e19608e MD |
76 | unsigned long long sched_clock(void) |
77 | { | |
1fa9be72 | 78 | return cycles_2_ns(bfin_read_cycles(&bfin_cs_cycles)); |
8e19608e MD |
79 | } |
80 | ||
1fa9be72 | 81 | static int __init bfin_cs_cycles_init(void) |
8b5f79f9 VM |
82 | { |
83 | set_cyc2ns_scale(get_cclk() / 1000); | |
84 | ||
1fa9be72 GY |
85 | bfin_cs_cycles.mult = \ |
86 | clocksource_hz2mult(get_cclk(), bfin_cs_cycles.shift); | |
8b5f79f9 | 87 | |
1fa9be72 | 88 | if (clocksource_register(&bfin_cs_cycles)) |
8b5f79f9 VM |
89 | panic("failed to register clocksource"); |
90 | ||
91 | return 0; | |
92 | } | |
1fa9be72 GY |
93 | #else |
94 | # define bfin_cs_cycles_init() | |
95 | #endif | |
96 | ||
97 | #ifdef CONFIG_GPTMR0_CLOCKSOURCE | |
98 | ||
99 | void __init setup_gptimer0(void) | |
100 | { | |
101 | disable_gptimers(TIMER0bit); | |
102 | ||
103 | set_gptimer_config(TIMER0_id, \ | |
104 | TIMER_OUT_DIS | TIMER_PERIOD_CNT | TIMER_MODE_PWM); | |
105 | set_gptimer_period(TIMER0_id, -1); | |
106 | set_gptimer_pwidth(TIMER0_id, -2); | |
107 | SSYNC(); | |
108 | enable_gptimers(TIMER0bit); | |
109 | } | |
110 | ||
111 | static cycle_t bfin_read_gptimer0(void) | |
112 | { | |
113 | return bfin_read_TIMER0_COUNTER(); | |
114 | } | |
115 | ||
116 | static struct clocksource bfin_cs_gptimer0 = { | |
117 | .name = "bfin_cs_gptimer0", | |
118 | .rating = 400, | |
119 | .read = bfin_read_gptimer0, | |
120 | .mask = CLOCKSOURCE_MASK(32), | |
121 | .shift = 22, | |
122 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
123 | }; | |
124 | ||
125 | static int __init bfin_cs_gptimer0_init(void) | |
126 | { | |
127 | setup_gptimer0(); | |
8b5f79f9 | 128 | |
1fa9be72 GY |
129 | bfin_cs_gptimer0.mult = \ |
130 | clocksource_hz2mult(get_sclk(), bfin_cs_gptimer0.shift); | |
131 | ||
132 | if (clocksource_register(&bfin_cs_gptimer0)) | |
133 | panic("failed to register clocksource"); | |
134 | ||
135 | return 0; | |
136 | } | |
8b5f79f9 | 137 | #else |
1fa9be72 | 138 | # define bfin_cs_gptimer0_init() |
8b5f79f9 VM |
139 | #endif |
140 | ||
1fa9be72 GY |
141 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 |
142 | __attribute__((l1_text)) | |
143 | #endif | |
144 | irqreturn_t timer_interrupt(int irq, void *dev_id); | |
145 | ||
146 | static int bfin_timer_set_next_event(unsigned long, \ | |
147 | struct clock_event_device *); | |
148 | ||
149 | static void bfin_timer_set_mode(enum clock_event_mode, \ | |
150 | struct clock_event_device *); | |
151 | ||
152 | static struct clock_event_device clockevent_bfin = { | |
153 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | |
154 | .name = "bfin_gptimer0", | |
155 | .rating = 300, | |
156 | .irq = IRQ_TIMER0, | |
157 | #else | |
158 | .name = "bfin_core_timer", | |
159 | .rating = 350, | |
160 | .irq = IRQ_CORETMR, | |
161 | #endif | |
162 | .shift = 32, | |
163 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | |
164 | .set_next_event = bfin_timer_set_next_event, | |
165 | .set_mode = bfin_timer_set_mode, | |
166 | }; | |
167 | ||
168 | static struct irqaction bfin_timer_irq = { | |
169 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | |
170 | .name = "Blackfin GPTimer0", | |
171 | #else | |
172 | .name = "Blackfin CoreTimer", | |
173 | #endif | |
174 | .flags = IRQF_DISABLED | IRQF_TIMER | \ | |
175 | IRQF_IRQPOLL | IRQF_PERCPU, | |
176 | .handler = timer_interrupt, | |
177 | .dev_id = &clockevent_bfin, | |
178 | }; | |
179 | ||
180 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | |
8b5f79f9 VM |
181 | static int bfin_timer_set_next_event(unsigned long cycles, |
182 | struct clock_event_device *evt) | |
183 | { | |
1fa9be72 GY |
184 | disable_gptimers(TIMER0bit); |
185 | ||
186 | /* it starts counting three SCLK cycles after the TIMENx bit is set */ | |
187 | set_gptimer_pwidth(TIMER0_id, cycles - 3); | |
188 | enable_gptimers(TIMER0bit); | |
189 | return 0; | |
190 | } | |
191 | ||
192 | static void bfin_timer_set_mode(enum clock_event_mode mode, | |
193 | struct clock_event_device *evt) | |
194 | { | |
195 | switch (mode) { | |
196 | case CLOCK_EVT_MODE_PERIODIC: { | |
197 | set_gptimer_config(TIMER0_id, \ | |
198 | TIMER_OUT_DIS | TIMER_IRQ_ENA | \ | |
199 | TIMER_PERIOD_CNT | TIMER_MODE_PWM); | |
200 | set_gptimer_period(TIMER0_id, get_sclk() / HZ); | |
201 | set_gptimer_pwidth(TIMER0_id, get_sclk() / HZ - 1); | |
202 | enable_gptimers(TIMER0bit); | |
203 | break; | |
204 | } | |
205 | case CLOCK_EVT_MODE_ONESHOT: | |
206 | disable_gptimers(TIMER0bit); | |
207 | set_gptimer_config(TIMER0_id, \ | |
208 | TIMER_OUT_DIS | TIMER_IRQ_ENA | TIMER_MODE_PWM); | |
209 | set_gptimer_period(TIMER0_id, 0); | |
210 | break; | |
211 | case CLOCK_EVT_MODE_UNUSED: | |
212 | case CLOCK_EVT_MODE_SHUTDOWN: | |
213 | disable_gptimers(TIMER0bit); | |
214 | break; | |
215 | case CLOCK_EVT_MODE_RESUME: | |
216 | break; | |
217 | } | |
218 | } | |
219 | ||
220 | static void bfin_timer_ack(void) | |
221 | { | |
222 | set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0); | |
223 | } | |
224 | ||
225 | static void __init bfin_timer_init(void) | |
226 | { | |
227 | disable_gptimers(TIMER0bit); | |
228 | } | |
229 | ||
230 | static unsigned long __init bfin_clockevent_check(void) | |
231 | { | |
232 | setup_irq(IRQ_TIMER0, &bfin_timer_irq); | |
233 | return get_sclk(); | |
234 | } | |
235 | ||
236 | #else /* CONFIG_TICKSOURCE_CORETMR */ | |
237 | ||
238 | static int bfin_timer_set_next_event(unsigned long cycles, | |
239 | struct clock_event_device *evt) | |
240 | { | |
241 | bfin_write_TCNTL(TMPWR); | |
242 | CSYNC(); | |
8b5f79f9 VM |
243 | bfin_write_TCOUNT(cycles); |
244 | CSYNC(); | |
1fa9be72 | 245 | bfin_write_TCNTL(TMPWR | TMREN); |
8b5f79f9 VM |
246 | return 0; |
247 | } | |
248 | ||
249 | static void bfin_timer_set_mode(enum clock_event_mode mode, | |
1fa9be72 | 250 | struct clock_event_device *evt) |
8b5f79f9 VM |
251 | { |
252 | switch (mode) { | |
253 | case CLOCK_EVT_MODE_PERIODIC: { | |
e6c91b64 | 254 | unsigned long tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); |
8b5f79f9 VM |
255 | bfin_write_TCNTL(TMPWR); |
256 | CSYNC(); | |
1fa9be72 | 257 | bfin_write_TSCALE(TIME_SCALE - 1); |
8b5f79f9 VM |
258 | bfin_write_TPERIOD(tcount); |
259 | bfin_write_TCOUNT(tcount); | |
8b5f79f9 | 260 | CSYNC(); |
1fa9be72 | 261 | bfin_write_TCNTL(TMPWR | TMREN | TAUTORLD); |
8b5f79f9 VM |
262 | break; |
263 | } | |
264 | case CLOCK_EVT_MODE_ONESHOT: | |
1fa9be72 GY |
265 | bfin_write_TCNTL(TMPWR); |
266 | CSYNC(); | |
1bfb4b21 | 267 | bfin_write_TSCALE(TIME_SCALE - 1); |
1fa9be72 | 268 | bfin_write_TPERIOD(0); |
8b5f79f9 | 269 | bfin_write_TCOUNT(0); |
8b5f79f9 VM |
270 | break; |
271 | case CLOCK_EVT_MODE_UNUSED: | |
272 | case CLOCK_EVT_MODE_SHUTDOWN: | |
273 | bfin_write_TCNTL(0); | |
274 | CSYNC(); | |
275 | break; | |
276 | case CLOCK_EVT_MODE_RESUME: | |
277 | break; | |
278 | } | |
279 | } | |
280 | ||
1fa9be72 GY |
281 | static void bfin_timer_ack(void) |
282 | { | |
283 | } | |
284 | ||
8b5f79f9 VM |
285 | static void __init bfin_timer_init(void) |
286 | { | |
287 | /* power up the timer, but don't enable it just yet */ | |
288 | bfin_write_TCNTL(TMPWR); | |
289 | CSYNC(); | |
290 | ||
291 | /* | |
292 | * the TSCALE prescaler counter. | |
293 | */ | |
e6c91b64 | 294 | bfin_write_TSCALE(TIME_SCALE - 1); |
8b5f79f9 VM |
295 | bfin_write_TPERIOD(0); |
296 | bfin_write_TCOUNT(0); | |
297 | ||
8b5f79f9 VM |
298 | CSYNC(); |
299 | } | |
300 | ||
1fa9be72 GY |
301 | static unsigned long __init bfin_clockevent_check(void) |
302 | { | |
303 | setup_irq(IRQ_CORETMR, &bfin_timer_irq); | |
304 | return get_cclk() / TIME_SCALE; | |
305 | } | |
306 | ||
307 | void __init setup_core_timer(void) | |
308 | { | |
309 | bfin_timer_init(); | |
310 | bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL); | |
311 | } | |
312 | #endif /* CONFIG_TICKSOURCE_GPTMR0 */ | |
313 | ||
8b5f79f9 VM |
314 | /* |
315 | * timer_interrupt() needs to keep up the real-time clock, | |
316 | * as well as call the "do_timer()" routine every clocktick | |
317 | */ | |
8b5f79f9 VM |
318 | irqreturn_t timer_interrupt(int irq, void *dev_id) |
319 | { | |
320 | struct clock_event_device *evt = dev_id; | |
1fa9be72 | 321 | smp_mb(); |
8b5f79f9 | 322 | evt->event_handler(evt); |
1fa9be72 | 323 | bfin_timer_ack(); |
8b5f79f9 VM |
324 | return IRQ_HANDLED; |
325 | } | |
326 | ||
327 | static int __init bfin_clockevent_init(void) | |
328 | { | |
1bfb4b21 VM |
329 | unsigned long timer_clk; |
330 | ||
1fa9be72 | 331 | timer_clk = bfin_clockevent_check(); |
1bfb4b21 | 332 | |
8b5f79f9 VM |
333 | bfin_timer_init(); |
334 | ||
1bfb4b21 | 335 | clockevent_bfin.mult = div_sc(timer_clk, NSEC_PER_SEC, clockevent_bfin.shift); |
8b5f79f9 VM |
336 | clockevent_bfin.max_delta_ns = clockevent_delta2ns(-1, &clockevent_bfin); |
337 | clockevent_bfin.min_delta_ns = clockevent_delta2ns(100, &clockevent_bfin); | |
320ab2b0 | 338 | clockevent_bfin.cpumask = cpumask_of(0); |
8b5f79f9 VM |
339 | clockevents_register_device(&clockevent_bfin); |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | void __init time_init(void) | |
345 | { | |
346 | time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ | |
347 | ||
348 | #ifdef CONFIG_RTC_DRV_BFIN | |
349 | /* [#2663] hack to filter junk RTC values that would cause | |
350 | * userspace to have to deal with time values greater than | |
351 | * 2^31 seconds (which uClibc cannot cope with yet) | |
352 | */ | |
353 | if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { | |
354 | printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); | |
355 | bfin_write_RTC_STAT(0); | |
356 | } | |
357 | #endif | |
358 | ||
359 | /* Initialize xtime. From now on, xtime is updated with timer interrupts */ | |
360 | xtime.tv_sec = secs_since_1970; | |
361 | xtime.tv_nsec = 0; | |
362 | set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); | |
363 | ||
1fa9be72 GY |
364 | bfin_cs_cycles_init(); |
365 | bfin_cs_gptimer0_init(); | |
8b5f79f9 VM |
366 | bfin_clockevent_init(); |
367 | } |