diff options
Diffstat (limited to 'arch/sparc64/kernel/time.c')
-rw-r--r-- | arch/sparc64/kernel/time.c | 408 |
1 files changed, 218 insertions, 190 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 48e1217c1e42..21e3b0b9d9ce 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <linux/miscdevice.h> | 32 | #include <linux/miscdevice.h> |
33 | #include <linux/rtc.h> | 33 | #include <linux/rtc.h> |
34 | #include <linux/kernel_stat.h> | 34 | #include <linux/kernel_stat.h> |
35 | #include <linux/clockchips.h> | ||
36 | #include <linux/clocksource.h> | ||
35 | 37 | ||
36 | #include <asm/oplib.h> | 38 | #include <asm/oplib.h> |
37 | #include <asm/mostek.h> | 39 | #include <asm/mostek.h> |
@@ -61,6 +63,7 @@ static void __iomem *mstk48t59_regs; | |||
61 | static int set_rtc_mmss(unsigned long); | 63 | static int set_rtc_mmss(unsigned long); |
62 | 64 | ||
63 | #define TICK_PRIV_BIT (1UL << 63) | 65 | #define TICK_PRIV_BIT (1UL << 63) |
66 | #define TICKCMP_IRQ_BIT (1UL << 63) | ||
64 | 67 | ||
65 | #ifdef CONFIG_SMP | 68 | #ifdef CONFIG_SMP |
66 | unsigned long profile_pc(struct pt_regs *regs) | 69 | unsigned long profile_pc(struct pt_regs *regs) |
@@ -94,21 +97,22 @@ static void tick_disable_protection(void) | |||
94 | : "g2"); | 97 | : "g2"); |
95 | } | 98 | } |
96 | 99 | ||
97 | static void tick_init_tick(unsigned long offset) | 100 | static void tick_disable_irq(void) |
98 | { | 101 | { |
99 | tick_disable_protection(); | ||
100 | |||
101 | __asm__ __volatile__( | 102 | __asm__ __volatile__( |
102 | " rd %%tick, %%g1\n" | ||
103 | " andn %%g1, %1, %%g1\n" | ||
104 | " ba,pt %%xcc, 1f\n" | 103 | " ba,pt %%xcc, 1f\n" |
105 | " add %%g1, %0, %%g1\n" | 104 | " nop\n" |
106 | " .align 64\n" | 105 | " .align 64\n" |
107 | "1: wr %%g1, 0x0, %%tick_cmpr\n" | 106 | "1: wr %0, 0x0, %%tick_cmpr\n" |
108 | " rd %%tick_cmpr, %%g0" | 107 | " rd %%tick_cmpr, %%g0" |
109 | : /* no outputs */ | 108 | : /* no outputs */ |
110 | : "r" (offset), "r" (TICK_PRIV_BIT) | 109 | : "r" (TICKCMP_IRQ_BIT)); |
111 | : "g1"); | 110 | } |
111 | |||
112 | static void tick_init_tick(void) | ||
113 | { | ||
114 | tick_disable_protection(); | ||
115 | tick_disable_irq(); | ||
112 | } | 116 | } |
113 | 117 | ||
114 | static unsigned long tick_get_tick(void) | 118 | static unsigned long tick_get_tick(void) |
@@ -122,20 +126,14 @@ static unsigned long tick_get_tick(void) | |||
122 | return ret & ~TICK_PRIV_BIT; | 126 | return ret & ~TICK_PRIV_BIT; |
123 | } | 127 | } |
124 | 128 | ||
125 | static unsigned long tick_get_compare(void) | 129 | static int tick_add_compare(unsigned long adj) |
126 | { | 130 | { |
127 | unsigned long ret; | 131 | unsigned long orig_tick, new_tick, new_compare; |
128 | 132 | ||
129 | __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" | 133 | __asm__ __volatile__("rd %%tick, %0" |
130 | "mov %0, %0" | 134 | : "=r" (orig_tick)); |
131 | : "=r" (ret)); | ||
132 | 135 | ||
133 | return ret; | 136 | orig_tick &= ~TICKCMP_IRQ_BIT; |
134 | } | ||
135 | |||
136 | static unsigned long tick_add_compare(unsigned long adj) | ||
137 | { | ||
138 | unsigned long new_compare; | ||
139 | 137 | ||
140 | /* Workaround for Spitfire Errata (#54 I think??), I discovered | 138 | /* Workaround for Spitfire Errata (#54 I think??), I discovered |
141 | * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch | 139 | * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch |
@@ -146,44 +144,41 @@ static unsigned long tick_add_compare(unsigned long adj) | |||
146 | * at the start of an I-cache line, and perform a dummy | 144 | * at the start of an I-cache line, and perform a dummy |
147 | * read back from %tick_cmpr right after writing to it. -DaveM | 145 | * read back from %tick_cmpr right after writing to it. -DaveM |
148 | */ | 146 | */ |
149 | __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" | 147 | __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" |
150 | "ba,pt %%xcc, 1f\n\t" | 148 | " add %1, %2, %0\n\t" |
151 | " add %0, %1, %0\n\t" | ||
152 | ".align 64\n" | 149 | ".align 64\n" |
153 | "1:\n\t" | 150 | "1:\n\t" |
154 | "wr %0, 0, %%tick_cmpr\n\t" | 151 | "wr %0, 0, %%tick_cmpr\n\t" |
155 | "rd %%tick_cmpr, %%g0" | 152 | "rd %%tick_cmpr, %%g0\n\t" |
156 | : "=&r" (new_compare) | 153 | : "=r" (new_compare) |
157 | : "r" (adj)); | 154 | : "r" (orig_tick), "r" (adj)); |
155 | |||
156 | __asm__ __volatile__("rd %%tick, %0" | ||
157 | : "=r" (new_tick)); | ||
158 | new_tick &= ~TICKCMP_IRQ_BIT; | ||
158 | 159 | ||
159 | return new_compare; | 160 | return ((long)(new_tick - (orig_tick+adj))) > 0L; |
160 | } | 161 | } |
161 | 162 | ||
162 | static unsigned long tick_add_tick(unsigned long adj, unsigned long offset) | 163 | static unsigned long tick_add_tick(unsigned long adj) |
163 | { | 164 | { |
164 | unsigned long new_tick, tmp; | 165 | unsigned long new_tick; |
165 | 166 | ||
166 | /* Also need to handle Blackbird bug here too. */ | 167 | /* Also need to handle Blackbird bug here too. */ |
167 | __asm__ __volatile__("rd %%tick, %0\n\t" | 168 | __asm__ __volatile__("rd %%tick, %0\n\t" |
168 | "add %0, %2, %0\n\t" | 169 | "add %0, %1, %0\n\t" |
169 | "wrpr %0, 0, %%tick\n\t" | 170 | "wrpr %0, 0, %%tick\n\t" |
170 | "andn %0, %4, %1\n\t" | 171 | : "=&r" (new_tick) |
171 | "ba,pt %%xcc, 1f\n\t" | 172 | : "r" (adj)); |
172 | " add %1, %3, %1\n\t" | ||
173 | ".align 64\n" | ||
174 | "1:\n\t" | ||
175 | "wr %1, 0, %%tick_cmpr\n\t" | ||
176 | "rd %%tick_cmpr, %%g0" | ||
177 | : "=&r" (new_tick), "=&r" (tmp) | ||
178 | : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); | ||
179 | 173 | ||
180 | return new_tick; | 174 | return new_tick; |
181 | } | 175 | } |
182 | 176 | ||
183 | static struct sparc64_tick_ops tick_operations __read_mostly = { | 177 | static struct sparc64_tick_ops tick_operations __read_mostly = { |
178 | .name = "tick", | ||
184 | .init_tick = tick_init_tick, | 179 | .init_tick = tick_init_tick, |
180 | .disable_irq = tick_disable_irq, | ||
185 | .get_tick = tick_get_tick, | 181 | .get_tick = tick_get_tick, |
186 | .get_compare = tick_get_compare, | ||
187 | .add_tick = tick_add_tick, | 182 | .add_tick = tick_add_tick, |
188 | .add_compare = tick_add_compare, | 183 | .add_compare = tick_add_compare, |
189 | .softint_mask = 1UL << 0, | 184 | .softint_mask = 1UL << 0, |
@@ -191,7 +186,15 @@ static struct sparc64_tick_ops tick_operations __read_mostly = { | |||
191 | 186 | ||
192 | struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations; | 187 | struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations; |
193 | 188 | ||
194 | static void stick_init_tick(unsigned long offset) | 189 | static void stick_disable_irq(void) |
190 | { | ||
191 | __asm__ __volatile__( | ||
192 | "wr %0, 0x0, %%asr25" | ||
193 | : /* no outputs */ | ||
194 | : "r" (TICKCMP_IRQ_BIT)); | ||
195 | } | ||
196 | |||
197 | static void stick_init_tick(void) | ||
195 | { | 198 | { |
196 | /* Writes to the %tick and %stick register are not | 199 | /* Writes to the %tick and %stick register are not |
197 | * allowed on sun4v. The Hypervisor controls that | 200 | * allowed on sun4v. The Hypervisor controls that |
@@ -199,6 +202,7 @@ static void stick_init_tick(unsigned long offset) | |||
199 | */ | 202 | */ |
200 | if (tlb_type != hypervisor) { | 203 | if (tlb_type != hypervisor) { |
201 | tick_disable_protection(); | 204 | tick_disable_protection(); |
205 | tick_disable_irq(); | ||
202 | 206 | ||
203 | /* Let the user get at STICK too. */ | 207 | /* Let the user get at STICK too. */ |
204 | __asm__ __volatile__( | 208 | __asm__ __volatile__( |
@@ -210,14 +214,7 @@ static void stick_init_tick(unsigned long offset) | |||
210 | : "g1", "g2"); | 214 | : "g1", "g2"); |
211 | } | 215 | } |
212 | 216 | ||
213 | __asm__ __volatile__( | 217 | stick_disable_irq(); |
214 | " rd %%asr24, %%g1\n" | ||
215 | " andn %%g1, %1, %%g1\n" | ||
216 | " add %%g1, %0, %%g1\n" | ||
217 | " wr %%g1, 0x0, %%asr25" | ||
218 | : /* no outputs */ | ||
219 | : "r" (offset), "r" (TICK_PRIV_BIT) | ||
220 | : "g1"); | ||
221 | } | 218 | } |
222 | 219 | ||
223 | static unsigned long stick_get_tick(void) | 220 | static unsigned long stick_get_tick(void) |
@@ -230,49 +227,43 @@ static unsigned long stick_get_tick(void) | |||
230 | return ret & ~TICK_PRIV_BIT; | 227 | return ret & ~TICK_PRIV_BIT; |
231 | } | 228 | } |
232 | 229 | ||
233 | static unsigned long stick_get_compare(void) | 230 | static unsigned long stick_add_tick(unsigned long adj) |
234 | { | ||
235 | unsigned long ret; | ||
236 | |||
237 | __asm__ __volatile__("rd %%asr25, %0" | ||
238 | : "=r" (ret)); | ||
239 | |||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | static unsigned long stick_add_tick(unsigned long adj, unsigned long offset) | ||
244 | { | 231 | { |
245 | unsigned long new_tick, tmp; | 232 | unsigned long new_tick; |
246 | 233 | ||
247 | __asm__ __volatile__("rd %%asr24, %0\n\t" | 234 | __asm__ __volatile__("rd %%asr24, %0\n\t" |
248 | "add %0, %2, %0\n\t" | 235 | "add %0, %1, %0\n\t" |
249 | "wr %0, 0, %%asr24\n\t" | 236 | "wr %0, 0, %%asr24\n\t" |
250 | "andn %0, %4, %1\n\t" | 237 | : "=&r" (new_tick) |
251 | "add %1, %3, %1\n\t" | 238 | : "r" (adj)); |
252 | "wr %1, 0, %%asr25" | ||
253 | : "=&r" (new_tick), "=&r" (tmp) | ||
254 | : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); | ||
255 | 239 | ||
256 | return new_tick; | 240 | return new_tick; |
257 | } | 241 | } |
258 | 242 | ||
259 | static unsigned long stick_add_compare(unsigned long adj) | 243 | static int stick_add_compare(unsigned long adj) |
260 | { | 244 | { |
261 | unsigned long new_compare; | 245 | unsigned long orig_tick, new_tick; |
262 | 246 | ||
263 | __asm__ __volatile__("rd %%asr25, %0\n\t" | 247 | __asm__ __volatile__("rd %%asr24, %0" |
264 | "add %0, %1, %0\n\t" | 248 | : "=r" (orig_tick)); |
265 | "wr %0, 0, %%asr25" | 249 | orig_tick &= ~TICKCMP_IRQ_BIT; |
266 | : "=&r" (new_compare) | 250 | |
267 | : "r" (adj)); | 251 | __asm__ __volatile__("wr %0, 0, %%asr25" |
252 | : /* no outputs */ | ||
253 | : "r" (orig_tick + adj)); | ||
254 | |||
255 | __asm__ __volatile__("rd %%asr24, %0" | ||
256 | : "=r" (new_tick)); | ||
257 | new_tick &= ~TICKCMP_IRQ_BIT; | ||
268 | 258 | ||
269 | return new_compare; | 259 | return ((long)(new_tick - (orig_tick+adj))) > 0L; |
270 | } | 260 | } |
271 | 261 | ||
272 | static struct sparc64_tick_ops stick_operations __read_mostly = { | 262 | static struct sparc64_tick_ops stick_operations __read_mostly = { |
263 | .name = "stick", | ||
273 | .init_tick = stick_init_tick, | 264 | .init_tick = stick_init_tick, |
265 | .disable_irq = stick_disable_irq, | ||
274 | .get_tick = stick_get_tick, | 266 | .get_tick = stick_get_tick, |
275 | .get_compare = stick_get_compare, | ||
276 | .add_tick = stick_add_tick, | 267 | .add_tick = stick_add_tick, |
277 | .add_compare = stick_add_compare, | 268 | .add_compare = stick_add_compare, |
278 | .softint_mask = 1UL << 16, | 269 | .softint_mask = 1UL << 16, |
@@ -321,20 +312,6 @@ static unsigned long __hbird_read_stick(void) | |||
321 | return ret; | 312 | return ret; |
322 | } | 313 | } |
323 | 314 | ||
324 | static unsigned long __hbird_read_compare(void) | ||
325 | { | ||
326 | unsigned long low, high; | ||
327 | unsigned long addr = HBIRD_STICKCMP_ADDR; | ||
328 | |||
329 | __asm__ __volatile__("ldxa [%2] %3, %0\n\t" | ||
330 | "add %2, 0x8, %2\n\t" | ||
331 | "ldxa [%2] %3, %1" | ||
332 | : "=&r" (low), "=&r" (high), "=&r" (addr) | ||
333 | : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr)); | ||
334 | |||
335 | return (high << 32UL) | low; | ||
336 | } | ||
337 | |||
338 | static void __hbird_write_stick(unsigned long val) | 315 | static void __hbird_write_stick(unsigned long val) |
339 | { | 316 | { |
340 | unsigned long low = (val & 0xffffffffUL); | 317 | unsigned long low = (val & 0xffffffffUL); |
@@ -365,10 +342,13 @@ static void __hbird_write_compare(unsigned long val) | |||
365 | "i" (ASI_PHYS_BYPASS_EC_E)); | 342 | "i" (ASI_PHYS_BYPASS_EC_E)); |
366 | } | 343 | } |
367 | 344 | ||
368 | static void hbtick_init_tick(unsigned long offset) | 345 | static void hbtick_disable_irq(void) |
369 | { | 346 | { |
370 | unsigned long val; | 347 | __hbird_write_compare(TICKCMP_IRQ_BIT); |
348 | } | ||
371 | 349 | ||
350 | static void hbtick_init_tick(void) | ||
351 | { | ||
372 | tick_disable_protection(); | 352 | tick_disable_protection(); |
373 | 353 | ||
374 | /* XXX This seems to be necessary to 'jumpstart' Hummingbird | 354 | /* XXX This seems to be necessary to 'jumpstart' Hummingbird |
@@ -378,8 +358,7 @@ static void hbtick_init_tick(unsigned long offset) | |||
378 | */ | 358 | */ |
379 | __hbird_write_stick(__hbird_read_stick()); | 359 | __hbird_write_stick(__hbird_read_stick()); |
380 | 360 | ||
381 | val = __hbird_read_stick() & ~TICK_PRIV_BIT; | 361 | hbtick_disable_irq(); |
382 | __hbird_write_compare(val + offset); | ||
383 | } | 362 | } |
384 | 363 | ||
385 | static unsigned long hbtick_get_tick(void) | 364 | static unsigned long hbtick_get_tick(void) |
@@ -387,45 +366,40 @@ static unsigned long hbtick_get_tick(void) | |||
387 | return __hbird_read_stick() & ~TICK_PRIV_BIT; | 366 | return __hbird_read_stick() & ~TICK_PRIV_BIT; |
388 | } | 367 | } |
389 | 368 | ||
390 | static unsigned long hbtick_get_compare(void) | 369 | static unsigned long hbtick_add_tick(unsigned long adj) |
391 | { | ||
392 | return __hbird_read_compare(); | ||
393 | } | ||
394 | |||
395 | static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset) | ||
396 | { | 370 | { |
397 | unsigned long val; | 371 | unsigned long val; |
398 | 372 | ||
399 | val = __hbird_read_stick() + adj; | 373 | val = __hbird_read_stick() + adj; |
400 | __hbird_write_stick(val); | 374 | __hbird_write_stick(val); |
401 | 375 | ||
402 | val &= ~TICK_PRIV_BIT; | ||
403 | __hbird_write_compare(val + offset); | ||
404 | |||
405 | return val; | 376 | return val; |
406 | } | 377 | } |
407 | 378 | ||
408 | static unsigned long hbtick_add_compare(unsigned long adj) | 379 | static int hbtick_add_compare(unsigned long adj) |
409 | { | 380 | { |
410 | unsigned long val = __hbird_read_compare() + adj; | 381 | unsigned long val = __hbird_read_stick(); |
382 | unsigned long val2; | ||
411 | 383 | ||
412 | val &= ~TICK_PRIV_BIT; | 384 | val &= ~TICKCMP_IRQ_BIT; |
385 | val += adj; | ||
413 | __hbird_write_compare(val); | 386 | __hbird_write_compare(val); |
414 | 387 | ||
415 | return val; | 388 | val2 = __hbird_read_stick() & ~TICKCMP_IRQ_BIT; |
389 | |||
390 | return ((long)(val2 - val)) > 0L; | ||
416 | } | 391 | } |
417 | 392 | ||
418 | static struct sparc64_tick_ops hbtick_operations __read_mostly = { | 393 | static struct sparc64_tick_ops hbtick_operations __read_mostly = { |
394 | .name = "hbtick", | ||
419 | .init_tick = hbtick_init_tick, | 395 | .init_tick = hbtick_init_tick, |
396 | .disable_irq = hbtick_disable_irq, | ||
420 | .get_tick = hbtick_get_tick, | 397 | .get_tick = hbtick_get_tick, |
421 | .get_compare = hbtick_get_compare, | ||
422 | .add_tick = hbtick_add_tick, | 398 | .add_tick = hbtick_add_tick, |
423 | .add_compare = hbtick_add_compare, | 399 | .add_compare = hbtick_add_compare, |
424 | .softint_mask = 1UL << 0, | 400 | .softint_mask = 1UL << 0, |
425 | }; | 401 | }; |
426 | 402 | ||
427 | unsigned long timer_tick_offset __read_mostly; | ||
428 | |||
429 | static unsigned long timer_ticks_per_nsec_quotient __read_mostly; | 403 | static unsigned long timer_ticks_per_nsec_quotient __read_mostly; |
430 | 404 | ||
431 | #define TICK_SIZE (tick_nsec / 1000) | 405 | #define TICK_SIZE (tick_nsec / 1000) |
@@ -482,50 +456,6 @@ void notify_arch_cmos_timer(void) | |||
482 | mod_timer(&sync_cmos_timer, jiffies + 1); | 456 | mod_timer(&sync_cmos_timer, jiffies + 1); |
483 | } | 457 | } |
484 | 458 | ||
485 | void timer_interrupt(int irq, struct pt_regs *regs) | ||
486 | { | ||
487 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
488 | unsigned long ticks, compare, pstate; | ||
489 | unsigned long tick_mask = tick_ops->softint_mask; | ||
490 | |||
491 | clear_softint(tick_mask); | ||
492 | |||
493 | irq_enter(); | ||
494 | |||
495 | kstat_this_cpu.irqs[0]++; | ||
496 | |||
497 | do { | ||
498 | profile_tick(CPU_PROFILING); | ||
499 | update_process_times(user_mode(get_irq_regs())); | ||
500 | |||
501 | if (smp_processor_id() == boot_cpu_id) { | ||
502 | write_seqlock(&xtime_lock); | ||
503 | do_timer(1); | ||
504 | write_sequnlock(&xtime_lock); | ||
505 | } | ||
506 | |||
507 | /* Guarantee that the following sequences execute | ||
508 | * uninterrupted. | ||
509 | */ | ||
510 | __asm__ __volatile__("rdpr %%pstate, %0\n\t" | ||
511 | "wrpr %0, %1, %%pstate" | ||
512 | : "=r" (pstate) | ||
513 | : "i" (PSTATE_IE)); | ||
514 | |||
515 | compare = tick_ops->add_compare(timer_tick_offset); | ||
516 | ticks = tick_ops->get_tick(); | ||
517 | |||
518 | /* Restore PSTATE_IE. */ | ||
519 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" | ||
520 | : /* no outputs */ | ||
521 | : "r" (pstate)); | ||
522 | } while (unlikely(time_after_eq(ticks, compare))); | ||
523 | |||
524 | irq_exit(); | ||
525 | |||
526 | set_irq_regs(old_regs); | ||
527 | } | ||
528 | |||
529 | /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ | 459 | /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ |
530 | static void __init kick_start_clock(void) | 460 | static void __init kick_start_clock(void) |
531 | { | 461 | { |
@@ -923,7 +853,6 @@ static unsigned long sparc64_init_timers(void) | |||
923 | prop = of_find_property(dp, "stick-frequency", NULL); | 853 | prop = of_find_property(dp, "stick-frequency", NULL); |
924 | } | 854 | } |
925 | clock = *(unsigned int *) prop->value; | 855 | clock = *(unsigned int *) prop->value; |
926 | timer_tick_offset = clock / HZ; | ||
927 | 856 | ||
928 | #ifdef CONFIG_SMP | 857 | #ifdef CONFIG_SMP |
929 | smp_tick_init(); | 858 | smp_tick_init(); |
@@ -932,26 +861,6 @@ static unsigned long sparc64_init_timers(void) | |||
932 | return clock; | 861 | return clock; |
933 | } | 862 | } |
934 | 863 | ||
935 | static void sparc64_start_timers(void) | ||
936 | { | ||
937 | unsigned long pstate; | ||
938 | |||
939 | /* Guarantee that the following sequences execute | ||
940 | * uninterrupted. | ||
941 | */ | ||
942 | __asm__ __volatile__("rdpr %%pstate, %0\n\t" | ||
943 | "wrpr %0, %1, %%pstate" | ||
944 | : "=r" (pstate) | ||
945 | : "i" (PSTATE_IE)); | ||
946 | |||
947 | tick_ops->init_tick(timer_tick_offset); | ||
948 | |||
949 | /* Restore PSTATE_IE. */ | ||
950 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" | ||
951 | : /* no outputs */ | ||
952 | : "r" (pstate)); | ||
953 | } | ||
954 | |||
955 | struct freq_table { | 864 | struct freq_table { |
956 | unsigned long clock_tick_ref; | 865 | unsigned long clock_tick_ref; |
957 | unsigned int ref_freq; | 866 | unsigned int ref_freq; |
@@ -998,29 +907,148 @@ static struct notifier_block sparc64_cpufreq_notifier_block = { | |||
998 | 907 | ||
999 | #endif /* CONFIG_CPU_FREQ */ | 908 | #endif /* CONFIG_CPU_FREQ */ |
1000 | 909 | ||
1001 | static struct time_interpolator sparc64_cpu_interpolator = { | 910 | static int sparc64_next_event(unsigned long delta, |
1002 | .source = TIME_SOURCE_CPU, | 911 | struct clock_event_device *evt) |
1003 | .shift = 16, | 912 | { |
1004 | .mask = 0xffffffffffffffffLL | 913 | return tick_ops->add_compare(delta); |
914 | } | ||
915 | |||
916 | static void sparc64_timer_setup(enum clock_event_mode mode, | ||
917 | struct clock_event_device *evt) | ||
918 | { | ||
919 | switch (mode) { | ||
920 | case CLOCK_EVT_MODE_ONESHOT: | ||
921 | break; | ||
922 | |||
923 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
924 | tick_ops->disable_irq(); | ||
925 | break; | ||
926 | |||
927 | case CLOCK_EVT_MODE_PERIODIC: | ||
928 | case CLOCK_EVT_MODE_UNUSED: | ||
929 | WARN_ON(1); | ||
930 | break; | ||
931 | }; | ||
932 | } | ||
933 | |||
934 | static struct clock_event_device sparc64_clockevent = { | ||
935 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
936 | .set_mode = sparc64_timer_setup, | ||
937 | .set_next_event = sparc64_next_event, | ||
938 | .rating = 100, | ||
939 | .shift = 30, | ||
940 | .irq = -1, | ||
1005 | }; | 941 | }; |
942 | static DEFINE_PER_CPU(struct clock_event_device, sparc64_events); | ||
1006 | 943 | ||
1007 | /* The quotient formula is taken from the IA64 port. */ | 944 | void timer_interrupt(int irq, struct pt_regs *regs) |
1008 | #define SPARC64_NSEC_PER_CYC_SHIFT 10UL | ||
1009 | void __init time_init(void) | ||
1010 | { | 945 | { |
1011 | unsigned long clock = sparc64_init_timers(); | 946 | struct pt_regs *old_regs = set_irq_regs(regs); |
947 | unsigned long tick_mask = tick_ops->softint_mask; | ||
948 | int cpu = smp_processor_id(); | ||
949 | struct clock_event_device *evt = &per_cpu(sparc64_events, cpu); | ||
950 | |||
951 | clear_softint(tick_mask); | ||
952 | |||
953 | irq_enter(); | ||
954 | |||
955 | kstat_this_cpu.irqs[0]++; | ||
956 | |||
957 | if (unlikely(!evt->event_handler)) { | ||
958 | printk(KERN_WARNING | ||
959 | "Spurious SPARC64 timer interrupt on cpu %d\n", cpu); | ||
960 | } else | ||
961 | evt->event_handler(evt); | ||
962 | |||
963 | irq_exit(); | ||
964 | |||
965 | set_irq_regs(old_regs); | ||
966 | } | ||
1012 | 967 | ||
1013 | sparc64_cpu_interpolator.frequency = clock; | 968 | void __devinit setup_sparc64_timer(void) |
1014 | register_time_interpolator(&sparc64_cpu_interpolator); | 969 | { |
970 | struct clock_event_device *sevt; | ||
971 | unsigned long pstate; | ||
1015 | 972 | ||
1016 | /* Now that the interpolator is registered, it is | 973 | /* Guarantee that the following sequences execute |
1017 | * safe to start the timer ticking. | 974 | * uninterrupted. |
1018 | */ | 975 | */ |
1019 | sparc64_start_timers(); | 976 | __asm__ __volatile__("rdpr %%pstate, %0\n\t" |
977 | "wrpr %0, %1, %%pstate" | ||
978 | : "=r" (pstate) | ||
979 | : "i" (PSTATE_IE)); | ||
980 | |||
981 | tick_ops->init_tick(); | ||
982 | |||
983 | /* Restore PSTATE_IE. */ | ||
984 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" | ||
985 | : /* no outputs */ | ||
986 | : "r" (pstate)); | ||
987 | |||
988 | sevt = &__get_cpu_var(sparc64_events); | ||
989 | |||
990 | memcpy(sevt, &sparc64_clockevent, sizeof(*sevt)); | ||
991 | sevt->cpumask = cpumask_of_cpu(smp_processor_id()); | ||
992 | |||
993 | clockevents_register_device(sevt); | ||
994 | } | ||
995 | |||
996 | #define SPARC64_NSEC_PER_CYC_SHIFT 32UL | ||
997 | |||
998 | static struct clocksource clocksource_tick = { | ||
999 | .rating = 100, | ||
1000 | .mask = CLOCKSOURCE_MASK(64), | ||
1001 | .shift = 16, | ||
1002 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
1003 | }; | ||
1004 | |||
1005 | static void __init setup_clockevent_multiplier(unsigned long hz) | ||
1006 | { | ||
1007 | unsigned long mult, shift = 32; | ||
1008 | |||
1009 | while (1) { | ||
1010 | mult = div_sc(hz, NSEC_PER_SEC, shift); | ||
1011 | if (mult && (mult >> 32UL) == 0UL) | ||
1012 | break; | ||
1013 | |||
1014 | shift--; | ||
1015 | } | ||
1016 | |||
1017 | sparc64_clockevent.shift = shift; | ||
1018 | sparc64_clockevent.mult = mult; | ||
1019 | } | ||
1020 | |||
1021 | void __init time_init(void) | ||
1022 | { | ||
1023 | unsigned long clock = sparc64_init_timers(); | ||
1020 | 1024 | ||
1021 | timer_ticks_per_nsec_quotient = | 1025 | timer_ticks_per_nsec_quotient = |
1022 | (((NSEC_PER_SEC << SPARC64_NSEC_PER_CYC_SHIFT) + | 1026 | clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); |
1023 | (clock / 2)) / clock); | 1027 | |
1028 | clocksource_tick.name = tick_ops->name; | ||
1029 | clocksource_tick.mult = | ||
1030 | clocksource_hz2mult(clock, | ||
1031 | clocksource_tick.shift); | ||
1032 | clocksource_tick.read = tick_ops->get_tick; | ||
1033 | |||
1034 | printk("clocksource: mult[%x] shift[%d]\n", | ||
1035 | clocksource_tick.mult, clocksource_tick.shift); | ||
1036 | |||
1037 | clocksource_register(&clocksource_tick); | ||
1038 | |||
1039 | sparc64_clockevent.name = tick_ops->name; | ||
1040 | |||
1041 | setup_clockevent_multiplier(clock); | ||
1042 | |||
1043 | sparc64_clockevent.max_delta_ns = | ||
1044 | clockevent_delta2ns(0x7fffffffffffffff, &sparc64_clockevent); | ||
1045 | sparc64_clockevent.min_delta_ns = | ||
1046 | clockevent_delta2ns(0xF, &sparc64_clockevent); | ||
1047 | |||
1048 | printk("clockevent: mult[%lx] shift[%d]\n", | ||
1049 | sparc64_clockevent.mult, sparc64_clockevent.shift); | ||
1050 | |||
1051 | setup_sparc64_timer(); | ||
1024 | 1052 | ||
1025 | #ifdef CONFIG_CPU_FREQ | 1053 | #ifdef CONFIG_CPU_FREQ |
1026 | cpufreq_register_notifier(&sparc64_cpufreq_notifier_block, | 1054 | cpufreq_register_notifier(&sparc64_cpufreq_notifier_block, |