Commit | Line | Data |
---|---|---|
8b5f79f9 | 1 | /* |
8b5f79f9 VM |
2 | * Based on arm clockevents implementation and old bfin time tick. |
3 | * | |
96f1050d RG |
4 | * Copyright 2008-2009 Analog Devics Inc. |
5 | * 2008 GeoTechnologies | |
6 | * Vitja Makarov | |
8b5f79f9 | 7 | * |
96f1050d | 8 | * Licensed under the GPL-2 |
8b5f79f9 | 9 | */ |
96f1050d | 10 | |
8b5f79f9 VM |
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> |
60ffdb36 | 24 | #include <asm/nmi.h> |
8b5f79f9 | 25 | |
e6c91b64 MH |
26 | /* Accelerators for sched_clock() |
27 | * convert from cycles(64bits) => nanoseconds (64bits) | |
28 | * basic equation: | |
29 | * ns = cycles / (freq / ns_per_sec) | |
30 | * ns = cycles * (ns_per_sec / freq) | |
31 | * ns = cycles * (10^9 / (cpu_khz * 10^3)) | |
32 | * ns = cycles * (10^6 / cpu_khz) | |
33 | * | |
34 | * Then we use scaling math (suggested by george@mvista.com) to get: | |
35 | * ns = cycles * (10^6 * SC / cpu_khz) / SC | |
36 | * ns = cycles * cyc2ns_scale / SC | |
37 | * | |
38 | * And since SC is a constant power of two, we can convert the div | |
39 | * into a shift. | |
40 | * | |
41 | * We can use khz divisor instead of mhz to keep a better precision, since | |
42 | * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits. | |
43 | * (mathieu.desnoyers@polymtl.ca) | |
44 | * | |
45 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" | |
46 | */ | |
47 | ||
8b5f79f9 VM |
48 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ |
49 | ||
ceb33be9 YL |
50 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) |
51 | ||
ceb33be9 | 52 | static notrace cycle_t bfin_read_cycles(struct clocksource *cs) |
8b5f79f9 | 53 | { |
6c2b7072 | 54 | #ifdef CONFIG_CPU_FREQ |
1bfb4b21 | 55 | return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod); |
6c2b7072 GY |
56 | #else |
57 | return get_cycles(); | |
58 | #endif | |
8b5f79f9 VM |
59 | } |
60 | ||
1fa9be72 GY |
61 | static struct clocksource bfin_cs_cycles = { |
62 | .name = "bfin_cs_cycles", | |
e78feaae | 63 | .rating = 400, |
1fa9be72 | 64 | .read = bfin_read_cycles, |
8b5f79f9 | 65 | .mask = CLOCKSOURCE_MASK(64), |
29857124 | 66 | .shift = CYC2NS_SCALE_FACTOR, |
8b5f79f9 VM |
67 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
68 | }; | |
69 | ||
ceb33be9 | 70 | static inline unsigned long long bfin_cs_cycles_sched_clock(void) |
8e19608e | 71 | { |
c768a943 MF |
72 | return clocksource_cyc2ns(bfin_read_cycles(&bfin_cs_cycles), |
73 | bfin_cs_cycles.mult, bfin_cs_cycles.shift); | |
8e19608e MD |
74 | } |
75 | ||
1fa9be72 | 76 | static int __init bfin_cs_cycles_init(void) |
8b5f79f9 | 77 | { |
1fa9be72 GY |
78 | bfin_cs_cycles.mult = \ |
79 | clocksource_hz2mult(get_cclk(), bfin_cs_cycles.shift); | |
8b5f79f9 | 80 | |
1fa9be72 | 81 | if (clocksource_register(&bfin_cs_cycles)) |
8b5f79f9 VM |
82 | panic("failed to register clocksource"); |
83 | ||
84 | return 0; | |
85 | } | |
1fa9be72 GY |
86 | #else |
87 | # define bfin_cs_cycles_init() | |
88 | #endif | |
89 | ||
90 | #ifdef CONFIG_GPTMR0_CLOCKSOURCE | |
91 | ||
92 | void __init setup_gptimer0(void) | |
93 | { | |
94 | disable_gptimers(TIMER0bit); | |
95 | ||
96 | set_gptimer_config(TIMER0_id, \ | |
97 | TIMER_OUT_DIS | TIMER_PERIOD_CNT | TIMER_MODE_PWM); | |
98 | set_gptimer_period(TIMER0_id, -1); | |
99 | set_gptimer_pwidth(TIMER0_id, -2); | |
100 | SSYNC(); | |
101 | enable_gptimers(TIMER0bit); | |
102 | } | |
103 | ||
f7036d64 | 104 | static cycle_t bfin_read_gptimer0(struct clocksource *cs) |
1fa9be72 GY |
105 | { |
106 | return bfin_read_TIMER0_COUNTER(); | |
107 | } | |
108 | ||
109 | static struct clocksource bfin_cs_gptimer0 = { | |
110 | .name = "bfin_cs_gptimer0", | |
e78feaae | 111 | .rating = 350, |
1fa9be72 GY |
112 | .read = bfin_read_gptimer0, |
113 | .mask = CLOCKSOURCE_MASK(32), | |
29857124 | 114 | .shift = CYC2NS_SCALE_FACTOR, |
1fa9be72 GY |
115 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
116 | }; | |
117 | ||
ceb33be9 YL |
118 | static inline unsigned long long bfin_cs_gptimer0_sched_clock(void) |
119 | { | |
c768a943 MF |
120 | return clocksource_cyc2ns(bfin_read_TIMER0_COUNTER(), |
121 | bfin_cs_gptimer0.mult, bfin_cs_gptimer0.shift); | |
ceb33be9 YL |
122 | } |
123 | ||
1fa9be72 GY |
124 | static int __init bfin_cs_gptimer0_init(void) |
125 | { | |
126 | setup_gptimer0(); | |
8b5f79f9 | 127 | |
1fa9be72 GY |
128 | bfin_cs_gptimer0.mult = \ |
129 | clocksource_hz2mult(get_sclk(), bfin_cs_gptimer0.shift); | |
130 | ||
131 | if (clocksource_register(&bfin_cs_gptimer0)) | |
132 | panic("failed to register clocksource"); | |
133 | ||
134 | return 0; | |
135 | } | |
8b5f79f9 | 136 | #else |
1fa9be72 | 137 | # define bfin_cs_gptimer0_init() |
8b5f79f9 VM |
138 | #endif |
139 | ||
ceb33be9 YL |
140 | #if defined(CONFIG_GPTMR0_CLOCKSOURCE) || defined(CONFIG_CYCLES_CLOCKSOURCE) |
141 | /* prefer to use cycles since it has higher rating */ | |
142 | notrace unsigned long long sched_clock(void) | |
143 | { | |
144 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) | |
145 | return bfin_cs_cycles_sched_clock(); | |
146 | #else | |
147 | return bfin_cs_gptimer0_sched_clock(); | |
148 | #endif | |
149 | } | |
150 | #endif | |
151 | ||
1fa9be72 | 152 | #if defined(CONFIG_TICKSOURCE_GPTMR0) |
0d152c27 | 153 | static int bfin_gptmr0_set_next_event(unsigned long cycles, |
8b5f79f9 VM |
154 | struct clock_event_device *evt) |
155 | { | |
1fa9be72 GY |
156 | disable_gptimers(TIMER0bit); |
157 | ||
158 | /* it starts counting three SCLK cycles after the TIMENx bit is set */ | |
159 | set_gptimer_pwidth(TIMER0_id, cycles - 3); | |
160 | enable_gptimers(TIMER0bit); | |
161 | return 0; | |
162 | } | |
163 | ||
0d152c27 | 164 | static void bfin_gptmr0_set_mode(enum clock_event_mode mode, |
1fa9be72 GY |
165 | struct clock_event_device *evt) |
166 | { | |
167 | switch (mode) { | |
168 | case CLOCK_EVT_MODE_PERIODIC: { | |
169 | set_gptimer_config(TIMER0_id, \ | |
170 | TIMER_OUT_DIS | TIMER_IRQ_ENA | \ | |
171 | TIMER_PERIOD_CNT | TIMER_MODE_PWM); | |
172 | set_gptimer_period(TIMER0_id, get_sclk() / HZ); | |
173 | set_gptimer_pwidth(TIMER0_id, get_sclk() / HZ - 1); | |
174 | enable_gptimers(TIMER0bit); | |
175 | break; | |
176 | } | |
177 | case CLOCK_EVT_MODE_ONESHOT: | |
178 | disable_gptimers(TIMER0bit); | |
179 | set_gptimer_config(TIMER0_id, \ | |
180 | TIMER_OUT_DIS | TIMER_IRQ_ENA | TIMER_MODE_PWM); | |
181 | set_gptimer_period(TIMER0_id, 0); | |
182 | break; | |
183 | case CLOCK_EVT_MODE_UNUSED: | |
184 | case CLOCK_EVT_MODE_SHUTDOWN: | |
185 | disable_gptimers(TIMER0bit); | |
186 | break; | |
187 | case CLOCK_EVT_MODE_RESUME: | |
188 | break; | |
189 | } | |
190 | } | |
191 | ||
0d152c27 | 192 | static void bfin_gptmr0_ack(void) |
1fa9be72 GY |
193 | { |
194 | set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0); | |
195 | } | |
196 | ||
0d152c27 | 197 | static void __init bfin_gptmr0_init(void) |
1fa9be72 GY |
198 | { |
199 | disable_gptimers(TIMER0bit); | |
200 | } | |
201 | ||
0d152c27 YL |
202 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 |
203 | __attribute__((l1_text)) | |
204 | #endif | |
205 | irqreturn_t bfin_gptmr0_interrupt(int irq, void *dev_id) | |
1fa9be72 | 206 | { |
0d152c27 YL |
207 | struct clock_event_device *evt = dev_id; |
208 | smp_mb(); | |
0bf02ce6 MF |
209 | /* |
210 | * We want to ACK before we handle so that we can handle smaller timer | |
211 | * intervals. This way if the timer expires again while we're handling | |
212 | * things, we're more likely to see that 2nd int rather than swallowing | |
213 | * it by ACKing the int at the end of this handler. | |
214 | */ | |
0d152c27 | 215 | bfin_gptmr0_ack(); |
0bf02ce6 | 216 | evt->event_handler(evt); |
0d152c27 | 217 | return IRQ_HANDLED; |
1fa9be72 GY |
218 | } |
219 | ||
0d152c27 YL |
220 | static struct irqaction gptmr0_irq = { |
221 | .name = "Blackfin GPTimer0", | |
222 | .flags = IRQF_DISABLED | IRQF_TIMER | \ | |
223 | IRQF_IRQPOLL | IRQF_PERCPU, | |
224 | .handler = bfin_gptmr0_interrupt, | |
225 | }; | |
1fa9be72 | 226 | |
0d152c27 YL |
227 | static struct clock_event_device clockevent_gptmr0 = { |
228 | .name = "bfin_gptimer0", | |
229 | .rating = 300, | |
230 | .irq = IRQ_TIMER0, | |
231 | .shift = 32, | |
232 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | |
233 | .set_next_event = bfin_gptmr0_set_next_event, | |
234 | .set_mode = bfin_gptmr0_set_mode, | |
235 | }; | |
236 | ||
237 | static void __init bfin_gptmr0_clockevent_init(struct clock_event_device *evt) | |
238 | { | |
239 | unsigned long clock_tick; | |
240 | ||
241 | clock_tick = get_sclk(); | |
242 | evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift); | |
243 | evt->max_delta_ns = clockevent_delta2ns(-1, evt); | |
244 | evt->min_delta_ns = clockevent_delta2ns(100, evt); | |
245 | ||
246 | evt->cpumask = cpumask_of(0); | |
247 | ||
248 | clockevents_register_device(evt); | |
249 | } | |
250 | #endif /* CONFIG_TICKSOURCE_GPTMR0 */ | |
251 | ||
252 | #if defined(CONFIG_TICKSOURCE_CORETMR) | |
253 | /* per-cpu local core timer */ | |
254 | static DEFINE_PER_CPU(struct clock_event_device, coretmr_events); | |
255 | ||
256 | static int bfin_coretmr_set_next_event(unsigned long cycles, | |
1fa9be72 GY |
257 | struct clock_event_device *evt) |
258 | { | |
259 | bfin_write_TCNTL(TMPWR); | |
260 | CSYNC(); | |
8b5f79f9 VM |
261 | bfin_write_TCOUNT(cycles); |
262 | CSYNC(); | |
1fa9be72 | 263 | bfin_write_TCNTL(TMPWR | TMREN); |
8b5f79f9 VM |
264 | return 0; |
265 | } | |
266 | ||
0d152c27 | 267 | static void bfin_coretmr_set_mode(enum clock_event_mode mode, |
1fa9be72 | 268 | struct clock_event_device *evt) |
8b5f79f9 VM |
269 | { |
270 | switch (mode) { | |
271 | case CLOCK_EVT_MODE_PERIODIC: { | |
e6c91b64 | 272 | unsigned long tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); |
8b5f79f9 VM |
273 | bfin_write_TCNTL(TMPWR); |
274 | CSYNC(); | |
1fa9be72 | 275 | bfin_write_TSCALE(TIME_SCALE - 1); |
8b5f79f9 VM |
276 | bfin_write_TPERIOD(tcount); |
277 | bfin_write_TCOUNT(tcount); | |
8b5f79f9 | 278 | CSYNC(); |
1fa9be72 | 279 | bfin_write_TCNTL(TMPWR | TMREN | TAUTORLD); |
8b5f79f9 VM |
280 | break; |
281 | } | |
282 | case CLOCK_EVT_MODE_ONESHOT: | |
1fa9be72 GY |
283 | bfin_write_TCNTL(TMPWR); |
284 | CSYNC(); | |
1bfb4b21 | 285 | bfin_write_TSCALE(TIME_SCALE - 1); |
1fa9be72 | 286 | bfin_write_TPERIOD(0); |
8b5f79f9 | 287 | bfin_write_TCOUNT(0); |
8b5f79f9 VM |
288 | break; |
289 | case CLOCK_EVT_MODE_UNUSED: | |
290 | case CLOCK_EVT_MODE_SHUTDOWN: | |
291 | bfin_write_TCNTL(0); | |
292 | CSYNC(); | |
293 | break; | |
294 | case CLOCK_EVT_MODE_RESUME: | |
295 | break; | |
296 | } | |
297 | } | |
298 | ||
0d152c27 | 299 | void bfin_coretmr_init(void) |
8b5f79f9 VM |
300 | { |
301 | /* power up the timer, but don't enable it just yet */ | |
302 | bfin_write_TCNTL(TMPWR); | |
303 | CSYNC(); | |
304 | ||
0d152c27 | 305 | /* the TSCALE prescaler counter. */ |
e6c91b64 | 306 | bfin_write_TSCALE(TIME_SCALE - 1); |
8b5f79f9 VM |
307 | bfin_write_TPERIOD(0); |
308 | bfin_write_TCOUNT(0); | |
309 | ||
8b5f79f9 VM |
310 | CSYNC(); |
311 | } | |
312 | ||
0d152c27 YL |
313 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 |
314 | __attribute__((l1_text)) | |
315 | #endif | |
316 | irqreturn_t bfin_coretmr_interrupt(int irq, void *dev_id) | |
1fa9be72 | 317 | { |
0d152c27 YL |
318 | int cpu = smp_processor_id(); |
319 | struct clock_event_device *evt = &per_cpu(coretmr_events, cpu); | |
1fa9be72 | 320 | |
1fa9be72 | 321 | smp_mb(); |
8b5f79f9 | 322 | evt->event_handler(evt); |
60ffdb36 GY |
323 | |
324 | touch_nmi_watchdog(); | |
325 | ||
8b5f79f9 VM |
326 | return IRQ_HANDLED; |
327 | } | |
328 | ||
0d152c27 YL |
329 | static struct irqaction coretmr_irq = { |
330 | .name = "Blackfin CoreTimer", | |
331 | .flags = IRQF_DISABLED | IRQF_TIMER | \ | |
332 | IRQF_IRQPOLL | IRQF_PERCPU, | |
333 | .handler = bfin_coretmr_interrupt, | |
334 | }; | |
8b5f79f9 | 335 | |
0d152c27 YL |
336 | void bfin_coretmr_clockevent_init(void) |
337 | { | |
338 | unsigned long clock_tick; | |
339 | unsigned int cpu = smp_processor_id(); | |
340 | struct clock_event_device *evt = &per_cpu(coretmr_events, cpu); | |
341 | ||
342 | evt->name = "bfin_core_timer"; | |
343 | evt->rating = 350; | |
344 | evt->irq = -1; | |
345 | evt->shift = 32; | |
346 | evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | |
347 | evt->set_next_event = bfin_coretmr_set_next_event; | |
348 | evt->set_mode = bfin_coretmr_set_mode; | |
349 | ||
350 | clock_tick = get_cclk() / TIME_SCALE; | |
351 | evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift); | |
352 | evt->max_delta_ns = clockevent_delta2ns(-1, evt); | |
353 | evt->min_delta_ns = clockevent_delta2ns(100, evt); | |
354 | ||
355 | evt->cpumask = cpumask_of(cpu); | |
356 | ||
357 | clockevents_register_device(evt); | |
8b5f79f9 | 358 | } |
0d152c27 YL |
359 | #endif /* CONFIG_TICKSOURCE_CORETMR */ |
360 | ||
8b5f79f9 | 361 | |
cb0e9963 | 362 | void read_persistent_clock(struct timespec *ts) |
8b5f79f9 VM |
363 | { |
364 | time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ | |
cb0e9963 JS |
365 | ts->tv_sec = secs_since_1970; |
366 | ts->tv_nsec = 0; | |
367 | } | |
368 | ||
369 | void __init time_init(void) | |
370 | { | |
8b5f79f9 VM |
371 | |
372 | #ifdef CONFIG_RTC_DRV_BFIN | |
373 | /* [#2663] hack to filter junk RTC values that would cause | |
374 | * userspace to have to deal with time values greater than | |
375 | * 2^31 seconds (which uClibc cannot cope with yet) | |
376 | */ | |
377 | if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { | |
378 | printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); | |
379 | bfin_write_RTC_STAT(0); | |
380 | } | |
381 | #endif | |
382 | ||
1fa9be72 GY |
383 | bfin_cs_cycles_init(); |
384 | bfin_cs_gptimer0_init(); | |
0d152c27 YL |
385 | |
386 | #if defined(CONFIG_TICKSOURCE_CORETMR) | |
387 | bfin_coretmr_init(); | |
388 | setup_irq(IRQ_CORETMR, &coretmr_irq); | |
389 | bfin_coretmr_clockevent_init(); | |
390 | #endif | |
391 | ||
392 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | |
393 | bfin_gptmr0_init(); | |
394 | setup_irq(IRQ_TIMER0, &gptmr0_irq); | |
395 | gptmr0_irq.dev_id = &clockevent_gptmr0; | |
396 | bfin_gptmr0_clockevent_init(&clockevent_gptmr0); | |
397 | #endif | |
398 | ||
399 | #if !defined(CONFIG_TICKSOURCE_CORETMR) && !defined(CONFIG_TICKSOURCE_GPTMR0) | |
400 | # error at least one clock event device is required | |
401 | #endif | |
8b5f79f9 | 402 | } |