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