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