diff options
Diffstat (limited to 'arch/blackfin/kernel/time-ts.c')
-rw-r--r-- | arch/blackfin/kernel/time-ts.c | 242 |
1 files changed, 135 insertions, 107 deletions
diff --git a/arch/blackfin/kernel/time-ts.c b/arch/blackfin/kernel/time-ts.c index 359cfb1815ca..cb7a01d4f009 100644 --- a/arch/blackfin/kernel/time-ts.c +++ b/arch/blackfin/kernel/time-ts.c | |||
@@ -21,8 +21,7 @@ | |||
21 | #include <asm/blackfin.h> | 21 | #include <asm/blackfin.h> |
22 | #include <asm/time.h> | 22 | #include <asm/time.h> |
23 | #include <asm/gptimers.h> | 23 | #include <asm/gptimers.h> |
24 | 24 | #include <asm/nmi.h> | |
25 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) | ||
26 | 25 | ||
27 | /* Accelerators for sched_clock() | 26 | /* Accelerators for sched_clock() |
28 | * convert from cycles(64bits) => nanoseconds (64bits) | 27 | * convert from cycles(64bits) => nanoseconds (64bits) |
@@ -46,22 +45,17 @@ | |||
46 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" | 45 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" |
47 | */ | 46 | */ |
48 | 47 | ||
49 | static unsigned long cyc2ns_scale; | ||
50 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | 48 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ |
51 | 49 | ||
52 | static inline void set_cyc2ns_scale(unsigned long cpu_khz) | 50 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) |
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 | 51 | ||
62 | static cycle_t bfin_read_cycles(struct clocksource *cs) | 52 | static notrace cycle_t bfin_read_cycles(struct clocksource *cs) |
63 | { | 53 | { |
54 | #ifdef CONFIG_CPU_FREQ | ||
64 | return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod); | 55 | return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod); |
56 | #else | ||
57 | return get_cycles(); | ||
58 | #endif | ||
65 | } | 59 | } |
66 | 60 | ||
67 | static struct clocksource bfin_cs_cycles = { | 61 | static struct clocksource bfin_cs_cycles = { |
@@ -69,19 +63,18 @@ static struct clocksource bfin_cs_cycles = { | |||
69 | .rating = 400, | 63 | .rating = 400, |
70 | .read = bfin_read_cycles, | 64 | .read = bfin_read_cycles, |
71 | .mask = CLOCKSOURCE_MASK(64), | 65 | .mask = CLOCKSOURCE_MASK(64), |
72 | .shift = 22, | 66 | .shift = CYC2NS_SCALE_FACTOR, |
73 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 67 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
74 | }; | 68 | }; |
75 | 69 | ||
76 | unsigned long long sched_clock(void) | 70 | static inline unsigned long long bfin_cs_cycles_sched_clock(void) |
77 | { | 71 | { |
78 | return cycles_2_ns(bfin_read_cycles(&bfin_cs_cycles)); | 72 | return clocksource_cyc2ns(bfin_read_cycles(&bfin_cs_cycles), |
73 | bfin_cs_cycles.mult, bfin_cs_cycles.shift); | ||
79 | } | 74 | } |
80 | 75 | ||
81 | static int __init bfin_cs_cycles_init(void) | 76 | static int __init bfin_cs_cycles_init(void) |
82 | { | 77 | { |
83 | set_cyc2ns_scale(get_cclk() / 1000); | ||
84 | |||
85 | bfin_cs_cycles.mult = \ | 78 | bfin_cs_cycles.mult = \ |
86 | clocksource_hz2mult(get_cclk(), bfin_cs_cycles.shift); | 79 | clocksource_hz2mult(get_cclk(), bfin_cs_cycles.shift); |
87 | 80 | ||
@@ -108,7 +101,7 @@ void __init setup_gptimer0(void) | |||
108 | enable_gptimers(TIMER0bit); | 101 | enable_gptimers(TIMER0bit); |
109 | } | 102 | } |
110 | 103 | ||
111 | static cycle_t bfin_read_gptimer0(void) | 104 | static cycle_t bfin_read_gptimer0(struct clocksource *cs) |
112 | { | 105 | { |
113 | return bfin_read_TIMER0_COUNTER(); | 106 | return bfin_read_TIMER0_COUNTER(); |
114 | } | 107 | } |
@@ -118,10 +111,16 @@ static struct clocksource bfin_cs_gptimer0 = { | |||
118 | .rating = 350, | 111 | .rating = 350, |
119 | .read = bfin_read_gptimer0, | 112 | .read = bfin_read_gptimer0, |
120 | .mask = CLOCKSOURCE_MASK(32), | 113 | .mask = CLOCKSOURCE_MASK(32), |
121 | .shift = 22, | 114 | .shift = CYC2NS_SCALE_FACTOR, |
122 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 115 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
123 | }; | 116 | }; |
124 | 117 | ||
118 | static inline unsigned long long bfin_cs_gptimer0_sched_clock(void) | ||
119 | { | ||
120 | return clocksource_cyc2ns(bfin_read_TIMER0_COUNTER(), | ||
121 | bfin_cs_gptimer0.mult, bfin_cs_gptimer0.shift); | ||
122 | } | ||
123 | |||
125 | static int __init bfin_cs_gptimer0_init(void) | 124 | static int __init bfin_cs_gptimer0_init(void) |
126 | { | 125 | { |
127 | setup_gptimer0(); | 126 | setup_gptimer0(); |
@@ -138,47 +137,20 @@ static int __init bfin_cs_gptimer0_init(void) | |||
138 | # define bfin_cs_gptimer0_init() | 137 | # define bfin_cs_gptimer0_init() |
139 | #endif | 138 | #endif |
140 | 139 | ||
141 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 | 140 | #if defined(CONFIG_GPTMR0_CLOCKSOURCE) || defined(CONFIG_CYCLES_CLOCKSOURCE) |
142 | __attribute__((l1_text)) | 141 | /* prefer to use cycles since it has higher rating */ |
143 | #endif | 142 | notrace unsigned long long sched_clock(void) |
144 | irqreturn_t timer_interrupt(int irq, void *dev_id); | 143 | { |
145 | 144 | #if defined(CONFIG_CYCLES_CLOCKSOURCE) | |
146 | static int bfin_timer_set_next_event(unsigned long, \ | 145 | return bfin_cs_cycles_sched_clock(); |
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 | 146 | #else |
158 | .name = "bfin_core_timer", | 147 | return bfin_cs_gptimer0_sched_clock(); |
159 | .rating = 350, | ||
160 | .irq = IRQ_CORETMR, | ||
161 | #endif | 148 | #endif |
162 | .shift = 32, | 149 | } |
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 | 150 | #endif |
174 | .flags = IRQF_DISABLED | IRQF_TIMER | \ | ||
175 | IRQF_IRQPOLL | IRQF_PERCPU, | ||
176 | .handler = timer_interrupt, | ||
177 | .dev_id = &clockevent_bfin, | ||
178 | }; | ||
179 | 151 | ||
180 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | 152 | #if defined(CONFIG_TICKSOURCE_GPTMR0) |
181 | static int bfin_timer_set_next_event(unsigned long cycles, | 153 | static int bfin_gptmr0_set_next_event(unsigned long cycles, |
182 | struct clock_event_device *evt) | 154 | struct clock_event_device *evt) |
183 | { | 155 | { |
184 | disable_gptimers(TIMER0bit); | 156 | disable_gptimers(TIMER0bit); |
@@ -189,7 +161,7 @@ static int bfin_timer_set_next_event(unsigned long cycles, | |||
189 | return 0; | 161 | return 0; |
190 | } | 162 | } |
191 | 163 | ||
192 | static void bfin_timer_set_mode(enum clock_event_mode mode, | 164 | static void bfin_gptmr0_set_mode(enum clock_event_mode mode, |
193 | struct clock_event_device *evt) | 165 | struct clock_event_device *evt) |
194 | { | 166 | { |
195 | switch (mode) { | 167 | switch (mode) { |
@@ -217,25 +189,65 @@ static void bfin_timer_set_mode(enum clock_event_mode mode, | |||
217 | } | 189 | } |
218 | } | 190 | } |
219 | 191 | ||
220 | static void bfin_timer_ack(void) | 192 | static void bfin_gptmr0_ack(void) |
221 | { | 193 | { |
222 | set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0); | 194 | set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0); |
223 | } | 195 | } |
224 | 196 | ||
225 | static void __init bfin_timer_init(void) | 197 | static void __init bfin_gptmr0_init(void) |
226 | { | 198 | { |
227 | disable_gptimers(TIMER0bit); | 199 | disable_gptimers(TIMER0bit); |
228 | } | 200 | } |
229 | 201 | ||
230 | static unsigned long __init bfin_clockevent_check(void) | 202 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 |
203 | __attribute__((l1_text)) | ||
204 | #endif | ||
205 | irqreturn_t bfin_gptmr0_interrupt(int irq, void *dev_id) | ||
231 | { | 206 | { |
232 | setup_irq(IRQ_TIMER0, &bfin_timer_irq); | 207 | struct clock_event_device *evt = dev_id; |
233 | return get_sclk(); | 208 | smp_mb(); |
209 | evt->event_handler(evt); | ||
210 | bfin_gptmr0_ack(); | ||
211 | return IRQ_HANDLED; | ||
234 | } | 212 | } |
235 | 213 | ||
236 | #else /* CONFIG_TICKSOURCE_CORETMR */ | 214 | static struct irqaction gptmr0_irq = { |
215 | .name = "Blackfin GPTimer0", | ||
216 | .flags = IRQF_DISABLED | IRQF_TIMER | \ | ||
217 | IRQF_IRQPOLL | IRQF_PERCPU, | ||
218 | .handler = bfin_gptmr0_interrupt, | ||
219 | }; | ||
237 | 220 | ||
238 | static int bfin_timer_set_next_event(unsigned long cycles, | 221 | static struct clock_event_device clockevent_gptmr0 = { |
222 | .name = "bfin_gptimer0", | ||
223 | .rating = 300, | ||
224 | .irq = IRQ_TIMER0, | ||
225 | .shift = 32, | ||
226 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
227 | .set_next_event = bfin_gptmr0_set_next_event, | ||
228 | .set_mode = bfin_gptmr0_set_mode, | ||
229 | }; | ||
230 | |||
231 | static void __init bfin_gptmr0_clockevent_init(struct clock_event_device *evt) | ||
232 | { | ||
233 | unsigned long clock_tick; | ||
234 | |||
235 | clock_tick = get_sclk(); | ||
236 | evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift); | ||
237 | evt->max_delta_ns = clockevent_delta2ns(-1, evt); | ||
238 | evt->min_delta_ns = clockevent_delta2ns(100, evt); | ||
239 | |||
240 | evt->cpumask = cpumask_of(0); | ||
241 | |||
242 | clockevents_register_device(evt); | ||
243 | } | ||
244 | #endif /* CONFIG_TICKSOURCE_GPTMR0 */ | ||
245 | |||
246 | #if defined(CONFIG_TICKSOURCE_CORETMR) | ||
247 | /* per-cpu local core timer */ | ||
248 | static DEFINE_PER_CPU(struct clock_event_device, coretmr_events); | ||
249 | |||
250 | static int bfin_coretmr_set_next_event(unsigned long cycles, | ||
239 | struct clock_event_device *evt) | 251 | struct clock_event_device *evt) |
240 | { | 252 | { |
241 | bfin_write_TCNTL(TMPWR); | 253 | bfin_write_TCNTL(TMPWR); |
@@ -246,7 +258,7 @@ static int bfin_timer_set_next_event(unsigned long cycles, | |||
246 | return 0; | 258 | return 0; |
247 | } | 259 | } |
248 | 260 | ||
249 | static void bfin_timer_set_mode(enum clock_event_mode mode, | 261 | static void bfin_coretmr_set_mode(enum clock_event_mode mode, |
250 | struct clock_event_device *evt) | 262 | struct clock_event_device *evt) |
251 | { | 263 | { |
252 | switch (mode) { | 264 | switch (mode) { |
@@ -278,19 +290,13 @@ static void bfin_timer_set_mode(enum clock_event_mode mode, | |||
278 | } | 290 | } |
279 | } | 291 | } |
280 | 292 | ||
281 | static void bfin_timer_ack(void) | 293 | void bfin_coretmr_init(void) |
282 | { | ||
283 | } | ||
284 | |||
285 | static void __init bfin_timer_init(void) | ||
286 | { | 294 | { |
287 | /* power up the timer, but don't enable it just yet */ | 295 | /* power up the timer, but don't enable it just yet */ |
288 | bfin_write_TCNTL(TMPWR); | 296 | bfin_write_TCNTL(TMPWR); |
289 | CSYNC(); | 297 | CSYNC(); |
290 | 298 | ||
291 | /* | 299 | /* the TSCALE prescaler counter. */ |
292 | * the TSCALE prescaler counter. | ||
293 | */ | ||
294 | bfin_write_TSCALE(TIME_SCALE - 1); | 300 | bfin_write_TSCALE(TIME_SCALE - 1); |
295 | bfin_write_TPERIOD(0); | 301 | bfin_write_TPERIOD(0); |
296 | bfin_write_TCOUNT(0); | 302 | bfin_write_TCOUNT(0); |
@@ -298,48 +304,54 @@ static void __init bfin_timer_init(void) | |||
298 | CSYNC(); | 304 | CSYNC(); |
299 | } | 305 | } |
300 | 306 | ||
301 | static unsigned long __init bfin_clockevent_check(void) | 307 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 |
302 | { | 308 | __attribute__((l1_text)) |
303 | setup_irq(IRQ_CORETMR, &bfin_timer_irq); | 309 | #endif |
304 | return get_cclk() / TIME_SCALE; | 310 | irqreturn_t bfin_coretmr_interrupt(int irq, void *dev_id) |
305 | } | ||
306 | |||
307 | void __init setup_core_timer(void) | ||
308 | { | 311 | { |
309 | bfin_timer_init(); | 312 | int cpu = smp_processor_id(); |
310 | bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL); | 313 | struct clock_event_device *evt = &per_cpu(coretmr_events, cpu); |
311 | } | ||
312 | #endif /* CONFIG_TICKSOURCE_GPTMR0 */ | ||
313 | 314 | ||
314 | /* | ||
315 | * timer_interrupt() needs to keep up the real-time clock, | ||
316 | * as well as call the "do_timer()" routine every clocktick | ||
317 | */ | ||
318 | irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
319 | { | ||
320 | struct clock_event_device *evt = dev_id; | ||
321 | smp_mb(); | 315 | smp_mb(); |
322 | evt->event_handler(evt); | 316 | evt->event_handler(evt); |
323 | bfin_timer_ack(); | ||
324 | return IRQ_HANDLED; | ||
325 | } | ||
326 | 317 | ||
327 | static int __init bfin_clockevent_init(void) | 318 | touch_nmi_watchdog(); |
328 | { | ||
329 | unsigned long timer_clk; | ||
330 | |||
331 | timer_clk = bfin_clockevent_check(); | ||
332 | 319 | ||
333 | bfin_timer_init(); | 320 | return IRQ_HANDLED; |
321 | } | ||
334 | 322 | ||
335 | clockevent_bfin.mult = div_sc(timer_clk, NSEC_PER_SEC, clockevent_bfin.shift); | 323 | static struct irqaction coretmr_irq = { |
336 | clockevent_bfin.max_delta_ns = clockevent_delta2ns(-1, &clockevent_bfin); | 324 | .name = "Blackfin CoreTimer", |
337 | clockevent_bfin.min_delta_ns = clockevent_delta2ns(100, &clockevent_bfin); | 325 | .flags = IRQF_DISABLED | IRQF_TIMER | \ |
338 | clockevent_bfin.cpumask = cpumask_of(0); | 326 | IRQF_IRQPOLL | IRQF_PERCPU, |
339 | clockevents_register_device(&clockevent_bfin); | 327 | .handler = bfin_coretmr_interrupt, |
328 | }; | ||
340 | 329 | ||
341 | return 0; | 330 | void bfin_coretmr_clockevent_init(void) |
331 | { | ||
332 | unsigned long clock_tick; | ||
333 | unsigned int cpu = smp_processor_id(); | ||
334 | struct clock_event_device *evt = &per_cpu(coretmr_events, cpu); | ||
335 | |||
336 | evt->name = "bfin_core_timer"; | ||
337 | evt->rating = 350; | ||
338 | evt->irq = -1; | ||
339 | evt->shift = 32; | ||
340 | evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
341 | evt->set_next_event = bfin_coretmr_set_next_event; | ||
342 | evt->set_mode = bfin_coretmr_set_mode; | ||
343 | |||
344 | clock_tick = get_cclk() / TIME_SCALE; | ||
345 | evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift); | ||
346 | evt->max_delta_ns = clockevent_delta2ns(-1, evt); | ||
347 | evt->min_delta_ns = clockevent_delta2ns(100, evt); | ||
348 | |||
349 | evt->cpumask = cpumask_of(cpu); | ||
350 | |||
351 | clockevents_register_device(evt); | ||
342 | } | 352 | } |
353 | #endif /* CONFIG_TICKSOURCE_CORETMR */ | ||
354 | |||
343 | 355 | ||
344 | void __init time_init(void) | 356 | void __init time_init(void) |
345 | { | 357 | { |
@@ -363,5 +375,21 @@ void __init time_init(void) | |||
363 | 375 | ||
364 | bfin_cs_cycles_init(); | 376 | bfin_cs_cycles_init(); |
365 | bfin_cs_gptimer0_init(); | 377 | bfin_cs_gptimer0_init(); |
366 | bfin_clockevent_init(); | 378 | |
379 | #if defined(CONFIG_TICKSOURCE_CORETMR) | ||
380 | bfin_coretmr_init(); | ||
381 | setup_irq(IRQ_CORETMR, &coretmr_irq); | ||
382 | bfin_coretmr_clockevent_init(); | ||
383 | #endif | ||
384 | |||
385 | #if defined(CONFIG_TICKSOURCE_GPTMR0) | ||
386 | bfin_gptmr0_init(); | ||
387 | setup_irq(IRQ_TIMER0, &gptmr0_irq); | ||
388 | gptmr0_irq.dev_id = &clockevent_gptmr0; | ||
389 | bfin_gptmr0_clockevent_init(&clockevent_gptmr0); | ||
390 | #endif | ||
391 | |||
392 | #if !defined(CONFIG_TICKSOURCE_CORETMR) && !defined(CONFIG_TICKSOURCE_GPTMR0) | ||
393 | # error at least one clock event device is required | ||
394 | #endif | ||
367 | } | 395 | } |