diff options
Diffstat (limited to 'arch/sparc64/kernel/time.c')
-rw-r--r-- | arch/sparc64/kernel/time.c | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c new file mode 100644 index 000000000000..6a717d4d2bc5 --- /dev/null +++ b/arch/sparc64/kernel/time.c | |||
@@ -0,0 +1,1195 @@ | |||
1 | /* $Id: time.c,v 1.42 2002/01/23 14:33:55 davem Exp $ | ||
2 | * time.c: UltraSparc timer and TOD clock support. | ||
3 | * | ||
4 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | ||
5 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) | ||
6 | * | ||
7 | * Based largely on code which is: | ||
8 | * | ||
9 | * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/param.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/time.h> | ||
22 | #include <linux/timex.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/mc146818rtc.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/profile.h> | ||
28 | #include <linux/bcd.h> | ||
29 | #include <linux/jiffies.h> | ||
30 | #include <linux/cpufreq.h> | ||
31 | #include <linux/percpu.h> | ||
32 | #include <linux/profile.h> | ||
33 | |||
34 | #include <asm/oplib.h> | ||
35 | #include <asm/mostek.h> | ||
36 | #include <asm/timer.h> | ||
37 | #include <asm/irq.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/sbus.h> | ||
40 | #include <asm/fhc.h> | ||
41 | #include <asm/pbm.h> | ||
42 | #include <asm/ebus.h> | ||
43 | #include <asm/isa.h> | ||
44 | #include <asm/starfire.h> | ||
45 | #include <asm/smp.h> | ||
46 | #include <asm/sections.h> | ||
47 | #include <asm/cpudata.h> | ||
48 | |||
49 | DEFINE_SPINLOCK(mostek_lock); | ||
50 | DEFINE_SPINLOCK(rtc_lock); | ||
51 | unsigned long mstk48t02_regs = 0UL; | ||
52 | #ifdef CONFIG_PCI | ||
53 | unsigned long ds1287_regs = 0UL; | ||
54 | #endif | ||
55 | |||
56 | extern unsigned long wall_jiffies; | ||
57 | |||
58 | u64 jiffies_64 = INITIAL_JIFFIES; | ||
59 | |||
60 | EXPORT_SYMBOL(jiffies_64); | ||
61 | |||
62 | static unsigned long mstk48t08_regs = 0UL; | ||
63 | static unsigned long mstk48t59_regs = 0UL; | ||
64 | |||
65 | static int set_rtc_mmss(unsigned long); | ||
66 | |||
67 | static __init unsigned long dummy_get_tick(void) | ||
68 | { | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static __initdata struct sparc64_tick_ops dummy_tick_ops = { | ||
73 | .get_tick = dummy_get_tick, | ||
74 | }; | ||
75 | |||
76 | struct sparc64_tick_ops *tick_ops = &dummy_tick_ops; | ||
77 | |||
78 | #define TICK_PRIV_BIT (1UL << 63) | ||
79 | |||
80 | #ifdef CONFIG_SMP | ||
81 | unsigned long profile_pc(struct pt_regs *regs) | ||
82 | { | ||
83 | unsigned long pc = instruction_pointer(regs); | ||
84 | |||
85 | if (in_lock_functions(pc)) | ||
86 | return regs->u_regs[UREG_RETPC]; | ||
87 | return pc; | ||
88 | } | ||
89 | EXPORT_SYMBOL(profile_pc); | ||
90 | #endif | ||
91 | |||
92 | static void tick_disable_protection(void) | ||
93 | { | ||
94 | /* Set things up so user can access tick register for profiling | ||
95 | * purposes. Also workaround BB_ERRATA_1 by doing a dummy | ||
96 | * read back of %tick after writing it. | ||
97 | */ | ||
98 | __asm__ __volatile__( | ||
99 | " ba,pt %%xcc, 1f\n" | ||
100 | " nop\n" | ||
101 | " .align 64\n" | ||
102 | "1: rd %%tick, %%g2\n" | ||
103 | " add %%g2, 6, %%g2\n" | ||
104 | " andn %%g2, %0, %%g2\n" | ||
105 | " wrpr %%g2, 0, %%tick\n" | ||
106 | " rdpr %%tick, %%g0" | ||
107 | : /* no outputs */ | ||
108 | : "r" (TICK_PRIV_BIT) | ||
109 | : "g2"); | ||
110 | } | ||
111 | |||
112 | static void tick_init_tick(unsigned long offset) | ||
113 | { | ||
114 | tick_disable_protection(); | ||
115 | |||
116 | __asm__ __volatile__( | ||
117 | " rd %%tick, %%g1\n" | ||
118 | " andn %%g1, %1, %%g1\n" | ||
119 | " ba,pt %%xcc, 1f\n" | ||
120 | " add %%g1, %0, %%g1\n" | ||
121 | " .align 64\n" | ||
122 | "1: wr %%g1, 0x0, %%tick_cmpr\n" | ||
123 | " rd %%tick_cmpr, %%g0" | ||
124 | : /* no outputs */ | ||
125 | : "r" (offset), "r" (TICK_PRIV_BIT) | ||
126 | : "g1"); | ||
127 | } | ||
128 | |||
129 | static unsigned long tick_get_tick(void) | ||
130 | { | ||
131 | unsigned long ret; | ||
132 | |||
133 | __asm__ __volatile__("rd %%tick, %0\n\t" | ||
134 | "mov %0, %0" | ||
135 | : "=r" (ret)); | ||
136 | |||
137 | return ret & ~TICK_PRIV_BIT; | ||
138 | } | ||
139 | |||
140 | static unsigned long tick_get_compare(void) | ||
141 | { | ||
142 | unsigned long ret; | ||
143 | |||
144 | __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" | ||
145 | "mov %0, %0" | ||
146 | : "=r" (ret)); | ||
147 | |||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | static unsigned long tick_add_compare(unsigned long adj) | ||
152 | { | ||
153 | unsigned long new_compare; | ||
154 | |||
155 | /* Workaround for Spitfire Errata (#54 I think??), I discovered | ||
156 | * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch | ||
157 | * number 103640. | ||
158 | * | ||
159 | * On Blackbird writes to %tick_cmpr can fail, the | ||
160 | * workaround seems to be to execute the wr instruction | ||
161 | * at the start of an I-cache line, and perform a dummy | ||
162 | * read back from %tick_cmpr right after writing to it. -DaveM | ||
163 | */ | ||
164 | __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" | ||
165 | "ba,pt %%xcc, 1f\n\t" | ||
166 | " add %0, %1, %0\n\t" | ||
167 | ".align 64\n" | ||
168 | "1:\n\t" | ||
169 | "wr %0, 0, %%tick_cmpr\n\t" | ||
170 | "rd %%tick_cmpr, %%g0" | ||
171 | : "=&r" (new_compare) | ||
172 | : "r" (adj)); | ||
173 | |||
174 | return new_compare; | ||
175 | } | ||
176 | |||
177 | static unsigned long tick_add_tick(unsigned long adj, unsigned long offset) | ||
178 | { | ||
179 | unsigned long new_tick, tmp; | ||
180 | |||
181 | /* Also need to handle Blackbird bug here too. */ | ||
182 | __asm__ __volatile__("rd %%tick, %0\n\t" | ||
183 | "add %0, %2, %0\n\t" | ||
184 | "wrpr %0, 0, %%tick\n\t" | ||
185 | "andn %0, %4, %1\n\t" | ||
186 | "ba,pt %%xcc, 1f\n\t" | ||
187 | " add %1, %3, %1\n\t" | ||
188 | ".align 64\n" | ||
189 | "1:\n\t" | ||
190 | "wr %1, 0, %%tick_cmpr\n\t" | ||
191 | "rd %%tick_cmpr, %%g0" | ||
192 | : "=&r" (new_tick), "=&r" (tmp) | ||
193 | : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); | ||
194 | |||
195 | return new_tick; | ||
196 | } | ||
197 | |||
198 | static struct sparc64_tick_ops tick_operations = { | ||
199 | .init_tick = tick_init_tick, | ||
200 | .get_tick = tick_get_tick, | ||
201 | .get_compare = tick_get_compare, | ||
202 | .add_tick = tick_add_tick, | ||
203 | .add_compare = tick_add_compare, | ||
204 | .softint_mask = 1UL << 0, | ||
205 | }; | ||
206 | |||
207 | static void stick_init_tick(unsigned long offset) | ||
208 | { | ||
209 | tick_disable_protection(); | ||
210 | |||
211 | /* Let the user get at STICK too. */ | ||
212 | __asm__ __volatile__( | ||
213 | " rd %%asr24, %%g2\n" | ||
214 | " andn %%g2, %0, %%g2\n" | ||
215 | " wr %%g2, 0, %%asr24" | ||
216 | : /* no outputs */ | ||
217 | : "r" (TICK_PRIV_BIT) | ||
218 | : "g1", "g2"); | ||
219 | |||
220 | __asm__ __volatile__( | ||
221 | " rd %%asr24, %%g1\n" | ||
222 | " andn %%g1, %1, %%g1\n" | ||
223 | " add %%g1, %0, %%g1\n" | ||
224 | " wr %%g1, 0x0, %%asr25" | ||
225 | : /* no outputs */ | ||
226 | : "r" (offset), "r" (TICK_PRIV_BIT) | ||
227 | : "g1"); | ||
228 | } | ||
229 | |||
230 | static unsigned long stick_get_tick(void) | ||
231 | { | ||
232 | unsigned long ret; | ||
233 | |||
234 | __asm__ __volatile__("rd %%asr24, %0" | ||
235 | : "=r" (ret)); | ||
236 | |||
237 | return ret & ~TICK_PRIV_BIT; | ||
238 | } | ||
239 | |||
240 | static unsigned long stick_get_compare(void) | ||
241 | { | ||
242 | unsigned long ret; | ||
243 | |||
244 | __asm__ __volatile__("rd %%asr25, %0" | ||
245 | : "=r" (ret)); | ||
246 | |||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static unsigned long stick_add_tick(unsigned long adj, unsigned long offset) | ||
251 | { | ||
252 | unsigned long new_tick, tmp; | ||
253 | |||
254 | __asm__ __volatile__("rd %%asr24, %0\n\t" | ||
255 | "add %0, %2, %0\n\t" | ||
256 | "wr %0, 0, %%asr24\n\t" | ||
257 | "andn %0, %4, %1\n\t" | ||
258 | "add %1, %3, %1\n\t" | ||
259 | "wr %1, 0, %%asr25" | ||
260 | : "=&r" (new_tick), "=&r" (tmp) | ||
261 | : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT)); | ||
262 | |||
263 | return new_tick; | ||
264 | } | ||
265 | |||
266 | static unsigned long stick_add_compare(unsigned long adj) | ||
267 | { | ||
268 | unsigned long new_compare; | ||
269 | |||
270 | __asm__ __volatile__("rd %%asr25, %0\n\t" | ||
271 | "add %0, %1, %0\n\t" | ||
272 | "wr %0, 0, %%asr25" | ||
273 | : "=&r" (new_compare) | ||
274 | : "r" (adj)); | ||
275 | |||
276 | return new_compare; | ||
277 | } | ||
278 | |||
279 | static struct sparc64_tick_ops stick_operations = { | ||
280 | .init_tick = stick_init_tick, | ||
281 | .get_tick = stick_get_tick, | ||
282 | .get_compare = stick_get_compare, | ||
283 | .add_tick = stick_add_tick, | ||
284 | .add_compare = stick_add_compare, | ||
285 | .softint_mask = 1UL << 16, | ||
286 | }; | ||
287 | |||
288 | /* On Hummingbird the STICK/STICK_CMPR register is implemented | ||
289 | * in I/O space. There are two 64-bit registers each, the | ||
290 | * first holds the low 32-bits of the value and the second holds | ||
291 | * the high 32-bits. | ||
292 | * | ||
293 | * Since STICK is constantly updating, we have to access it carefully. | ||
294 | * | ||
295 | * The sequence we use to read is: | ||
296 | * 1) read low | ||
297 | * 2) read high | ||
298 | * 3) read low again, if it rolled over increment high by 1 | ||
299 | * | ||
300 | * Writing STICK safely is also tricky: | ||
301 | * 1) write low to zero | ||
302 | * 2) write high | ||
303 | * 3) write low | ||
304 | */ | ||
305 | #define HBIRD_STICKCMP_ADDR 0x1fe0000f060UL | ||
306 | #define HBIRD_STICK_ADDR 0x1fe0000f070UL | ||
307 | |||
308 | static unsigned long __hbird_read_stick(void) | ||
309 | { | ||
310 | unsigned long ret, tmp1, tmp2, tmp3; | ||
311 | unsigned long addr = HBIRD_STICK_ADDR; | ||
312 | |||
313 | __asm__ __volatile__("ldxa [%1] %5, %2\n\t" | ||
314 | "add %1, 0x8, %1\n\t" | ||
315 | "ldxa [%1] %5, %3\n\t" | ||
316 | "sub %1, 0x8, %1\n\t" | ||
317 | "ldxa [%1] %5, %4\n\t" | ||
318 | "cmp %4, %2\n\t" | ||
319 | "blu,a,pn %%xcc, 1f\n\t" | ||
320 | " add %3, 1, %3\n" | ||
321 | "1:\n\t" | ||
322 | "sllx %3, 32, %3\n\t" | ||
323 | "or %3, %4, %0\n\t" | ||
324 | : "=&r" (ret), "=&r" (addr), | ||
325 | "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3) | ||
326 | : "i" (ASI_PHYS_BYPASS_EC_E), "1" (addr)); | ||
327 | |||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | static unsigned long __hbird_read_compare(void) | ||
332 | { | ||
333 | unsigned long low, high; | ||
334 | unsigned long addr = HBIRD_STICKCMP_ADDR; | ||
335 | |||
336 | __asm__ __volatile__("ldxa [%2] %3, %0\n\t" | ||
337 | "add %2, 0x8, %2\n\t" | ||
338 | "ldxa [%2] %3, %1" | ||
339 | : "=&r" (low), "=&r" (high), "=&r" (addr) | ||
340 | : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr)); | ||
341 | |||
342 | return (high << 32UL) | low; | ||
343 | } | ||
344 | |||
345 | static void __hbird_write_stick(unsigned long val) | ||
346 | { | ||
347 | unsigned long low = (val & 0xffffffffUL); | ||
348 | unsigned long high = (val >> 32UL); | ||
349 | unsigned long addr = HBIRD_STICK_ADDR; | ||
350 | |||
351 | __asm__ __volatile__("stxa %%g0, [%0] %4\n\t" | ||
352 | "add %0, 0x8, %0\n\t" | ||
353 | "stxa %3, [%0] %4\n\t" | ||
354 | "sub %0, 0x8, %0\n\t" | ||
355 | "stxa %2, [%0] %4" | ||
356 | : "=&r" (addr) | ||
357 | : "0" (addr), "r" (low), "r" (high), | ||
358 | "i" (ASI_PHYS_BYPASS_EC_E)); | ||
359 | } | ||
360 | |||
361 | static void __hbird_write_compare(unsigned long val) | ||
362 | { | ||
363 | unsigned long low = (val & 0xffffffffUL); | ||
364 | unsigned long high = (val >> 32UL); | ||
365 | unsigned long addr = HBIRD_STICKCMP_ADDR + 0x8UL; | ||
366 | |||
367 | __asm__ __volatile__("stxa %3, [%0] %4\n\t" | ||
368 | "sub %0, 0x8, %0\n\t" | ||
369 | "stxa %2, [%0] %4" | ||
370 | : "=&r" (addr) | ||
371 | : "0" (addr), "r" (low), "r" (high), | ||
372 | "i" (ASI_PHYS_BYPASS_EC_E)); | ||
373 | } | ||
374 | |||
375 | static void hbtick_init_tick(unsigned long offset) | ||
376 | { | ||
377 | unsigned long val; | ||
378 | |||
379 | tick_disable_protection(); | ||
380 | |||
381 | /* XXX This seems to be necessary to 'jumpstart' Hummingbird | ||
382 | * XXX into actually sending STICK interrupts. I think because | ||
383 | * XXX of how we store %tick_cmpr in head.S this somehow resets the | ||
384 | * XXX {TICK + STICK} interrupt mux. -DaveM | ||
385 | */ | ||
386 | __hbird_write_stick(__hbird_read_stick()); | ||
387 | |||
388 | val = __hbird_read_stick() & ~TICK_PRIV_BIT; | ||
389 | __hbird_write_compare(val + offset); | ||
390 | } | ||
391 | |||
392 | static unsigned long hbtick_get_tick(void) | ||
393 | { | ||
394 | return __hbird_read_stick() & ~TICK_PRIV_BIT; | ||
395 | } | ||
396 | |||
397 | static unsigned long hbtick_get_compare(void) | ||
398 | { | ||
399 | return __hbird_read_compare(); | ||
400 | } | ||
401 | |||
402 | static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset) | ||
403 | { | ||
404 | unsigned long val; | ||
405 | |||
406 | val = __hbird_read_stick() + adj; | ||
407 | __hbird_write_stick(val); | ||
408 | |||
409 | val &= ~TICK_PRIV_BIT; | ||
410 | __hbird_write_compare(val + offset); | ||
411 | |||
412 | return val; | ||
413 | } | ||
414 | |||
415 | static unsigned long hbtick_add_compare(unsigned long adj) | ||
416 | { | ||
417 | unsigned long val = __hbird_read_compare() + adj; | ||
418 | |||
419 | val &= ~TICK_PRIV_BIT; | ||
420 | __hbird_write_compare(val); | ||
421 | |||
422 | return val; | ||
423 | } | ||
424 | |||
425 | static struct sparc64_tick_ops hbtick_operations = { | ||
426 | .init_tick = hbtick_init_tick, | ||
427 | .get_tick = hbtick_get_tick, | ||
428 | .get_compare = hbtick_get_compare, | ||
429 | .add_tick = hbtick_add_tick, | ||
430 | .add_compare = hbtick_add_compare, | ||
431 | .softint_mask = 1UL << 0, | ||
432 | }; | ||
433 | |||
434 | /* timer_interrupt() needs to keep up the real-time clock, | ||
435 | * as well as call the "do_timer()" routine every clocktick | ||
436 | * | ||
437 | * NOTE: On SUN5 systems the ticker interrupt comes in using 2 | ||
438 | * interrupts, one at level14 and one with softint bit 0. | ||
439 | */ | ||
440 | unsigned long timer_tick_offset; | ||
441 | unsigned long timer_tick_compare; | ||
442 | |||
443 | static unsigned long timer_ticks_per_nsec_quotient; | ||
444 | |||
445 | #define TICK_SIZE (tick_nsec / 1000) | ||
446 | |||
447 | static inline void timer_check_rtc(void) | ||
448 | { | ||
449 | /* last time the cmos clock got updated */ | ||
450 | static long last_rtc_update; | ||
451 | |||
452 | /* Determine when to update the Mostek clock. */ | ||
453 | if ((time_status & STA_UNSYNC) == 0 && | ||
454 | xtime.tv_sec > last_rtc_update + 660 && | ||
455 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | ||
456 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | ||
457 | if (set_rtc_mmss(xtime.tv_sec) == 0) | ||
458 | last_rtc_update = xtime.tv_sec; | ||
459 | else | ||
460 | last_rtc_update = xtime.tv_sec - 600; | ||
461 | /* do it again in 60 s */ | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) | ||
466 | { | ||
467 | unsigned long ticks, pstate; | ||
468 | |||
469 | write_seqlock(&xtime_lock); | ||
470 | |||
471 | do { | ||
472 | #ifndef CONFIG_SMP | ||
473 | profile_tick(CPU_PROFILING, regs); | ||
474 | update_process_times(user_mode(regs)); | ||
475 | #endif | ||
476 | do_timer(regs); | ||
477 | |||
478 | /* Guarantee that the following sequences execute | ||
479 | * uninterrupted. | ||
480 | */ | ||
481 | __asm__ __volatile__("rdpr %%pstate, %0\n\t" | ||
482 | "wrpr %0, %1, %%pstate" | ||
483 | : "=r" (pstate) | ||
484 | : "i" (PSTATE_IE)); | ||
485 | |||
486 | timer_tick_compare = tick_ops->add_compare(timer_tick_offset); | ||
487 | ticks = tick_ops->get_tick(); | ||
488 | |||
489 | /* Restore PSTATE_IE. */ | ||
490 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" | ||
491 | : /* no outputs */ | ||
492 | : "r" (pstate)); | ||
493 | } while (time_after_eq(ticks, timer_tick_compare)); | ||
494 | |||
495 | timer_check_rtc(); | ||
496 | |||
497 | write_sequnlock(&xtime_lock); | ||
498 | |||
499 | return IRQ_HANDLED; | ||
500 | } | ||
501 | |||
502 | #ifdef CONFIG_SMP | ||
503 | void timer_tick_interrupt(struct pt_regs *regs) | ||
504 | { | ||
505 | write_seqlock(&xtime_lock); | ||
506 | |||
507 | do_timer(regs); | ||
508 | |||
509 | /* | ||
510 | * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR. | ||
511 | */ | ||
512 | timer_tick_compare = tick_ops->get_compare() + timer_tick_offset; | ||
513 | |||
514 | timer_check_rtc(); | ||
515 | |||
516 | write_sequnlock(&xtime_lock); | ||
517 | } | ||
518 | #endif | ||
519 | |||
520 | /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ | ||
521 | static void __init kick_start_clock(void) | ||
522 | { | ||
523 | unsigned long regs = mstk48t02_regs; | ||
524 | u8 sec, tmp; | ||
525 | int i, count; | ||
526 | |||
527 | prom_printf("CLOCK: Clock was stopped. Kick start "); | ||
528 | |||
529 | spin_lock_irq(&mostek_lock); | ||
530 | |||
531 | /* Turn on the kick start bit to start the oscillator. */ | ||
532 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
533 | tmp |= MSTK_CREG_WRITE; | ||
534 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
535 | tmp = mostek_read(regs + MOSTEK_SEC); | ||
536 | tmp &= ~MSTK_STOP; | ||
537 | mostek_write(regs + MOSTEK_SEC, tmp); | ||
538 | tmp = mostek_read(regs + MOSTEK_HOUR); | ||
539 | tmp |= MSTK_KICK_START; | ||
540 | mostek_write(regs + MOSTEK_HOUR, tmp); | ||
541 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
542 | tmp &= ~MSTK_CREG_WRITE; | ||
543 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
544 | |||
545 | spin_unlock_irq(&mostek_lock); | ||
546 | |||
547 | /* Delay to allow the clock oscillator to start. */ | ||
548 | sec = MSTK_REG_SEC(regs); | ||
549 | for (i = 0; i < 3; i++) { | ||
550 | while (sec == MSTK_REG_SEC(regs)) | ||
551 | for (count = 0; count < 100000; count++) | ||
552 | /* nothing */ ; | ||
553 | prom_printf("."); | ||
554 | sec = MSTK_REG_SEC(regs); | ||
555 | } | ||
556 | prom_printf("\n"); | ||
557 | |||
558 | spin_lock_irq(&mostek_lock); | ||
559 | |||
560 | /* Turn off kick start and set a "valid" time and date. */ | ||
561 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
562 | tmp |= MSTK_CREG_WRITE; | ||
563 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
564 | tmp = mostek_read(regs + MOSTEK_HOUR); | ||
565 | tmp &= ~MSTK_KICK_START; | ||
566 | mostek_write(regs + MOSTEK_HOUR, tmp); | ||
567 | MSTK_SET_REG_SEC(regs,0); | ||
568 | MSTK_SET_REG_MIN(regs,0); | ||
569 | MSTK_SET_REG_HOUR(regs,0); | ||
570 | MSTK_SET_REG_DOW(regs,5); | ||
571 | MSTK_SET_REG_DOM(regs,1); | ||
572 | MSTK_SET_REG_MONTH(regs,8); | ||
573 | MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); | ||
574 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
575 | tmp &= ~MSTK_CREG_WRITE; | ||
576 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
577 | |||
578 | spin_unlock_irq(&mostek_lock); | ||
579 | |||
580 | /* Ensure the kick start bit is off. If it isn't, turn it off. */ | ||
581 | while (mostek_read(regs + MOSTEK_HOUR) & MSTK_KICK_START) { | ||
582 | prom_printf("CLOCK: Kick start still on!\n"); | ||
583 | |||
584 | spin_lock_irq(&mostek_lock); | ||
585 | |||
586 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
587 | tmp |= MSTK_CREG_WRITE; | ||
588 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
589 | |||
590 | tmp = mostek_read(regs + MOSTEK_HOUR); | ||
591 | tmp &= ~MSTK_KICK_START; | ||
592 | mostek_write(regs + MOSTEK_HOUR, tmp); | ||
593 | |||
594 | tmp = mostek_read(regs + MOSTEK_CREG); | ||
595 | tmp &= ~MSTK_CREG_WRITE; | ||
596 | mostek_write(regs + MOSTEK_CREG, tmp); | ||
597 | |||
598 | spin_unlock_irq(&mostek_lock); | ||
599 | } | ||
600 | |||
601 | prom_printf("CLOCK: Kick start procedure successful.\n"); | ||
602 | } | ||
603 | |||
604 | /* Return nonzero if the clock chip battery is low. */ | ||
605 | static int __init has_low_battery(void) | ||
606 | { | ||
607 | unsigned long regs = mstk48t02_regs; | ||
608 | u8 data1, data2; | ||
609 | |||
610 | spin_lock_irq(&mostek_lock); | ||
611 | |||
612 | data1 = mostek_read(regs + MOSTEK_EEPROM); /* Read some data. */ | ||
613 | mostek_write(regs + MOSTEK_EEPROM, ~data1); /* Write back the complement. */ | ||
614 | data2 = mostek_read(regs + MOSTEK_EEPROM); /* Read back the complement. */ | ||
615 | mostek_write(regs + MOSTEK_EEPROM, data1); /* Restore original value. */ | ||
616 | |||
617 | spin_unlock_irq(&mostek_lock); | ||
618 | |||
619 | return (data1 == data2); /* Was the write blocked? */ | ||
620 | } | ||
621 | |||
622 | /* Probe for the real time clock chip. */ | ||
623 | static void __init set_system_time(void) | ||
624 | { | ||
625 | unsigned int year, mon, day, hour, min, sec; | ||
626 | unsigned long mregs = mstk48t02_regs; | ||
627 | #ifdef CONFIG_PCI | ||
628 | unsigned long dregs = ds1287_regs; | ||
629 | #else | ||
630 | unsigned long dregs = 0UL; | ||
631 | #endif | ||
632 | u8 tmp; | ||
633 | |||
634 | if (!mregs && !dregs) { | ||
635 | prom_printf("Something wrong, clock regs not mapped yet.\n"); | ||
636 | prom_halt(); | ||
637 | } | ||
638 | |||
639 | if (mregs) { | ||
640 | spin_lock_irq(&mostek_lock); | ||
641 | |||
642 | /* Traditional Mostek chip. */ | ||
643 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
644 | tmp |= MSTK_CREG_READ; | ||
645 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
646 | |||
647 | sec = MSTK_REG_SEC(mregs); | ||
648 | min = MSTK_REG_MIN(mregs); | ||
649 | hour = MSTK_REG_HOUR(mregs); | ||
650 | day = MSTK_REG_DOM(mregs); | ||
651 | mon = MSTK_REG_MONTH(mregs); | ||
652 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); | ||
653 | } else { | ||
654 | int i; | ||
655 | |||
656 | /* Dallas 12887 RTC chip. */ | ||
657 | |||
658 | /* Stolen from arch/i386/kernel/time.c, see there for | ||
659 | * credits and descriptive comments. | ||
660 | */ | ||
661 | for (i = 0; i < 1000000; i++) { | ||
662 | if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) | ||
663 | break; | ||
664 | udelay(10); | ||
665 | } | ||
666 | for (i = 0; i < 1000000; i++) { | ||
667 | if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) | ||
668 | break; | ||
669 | udelay(10); | ||
670 | } | ||
671 | do { | ||
672 | sec = CMOS_READ(RTC_SECONDS); | ||
673 | min = CMOS_READ(RTC_MINUTES); | ||
674 | hour = CMOS_READ(RTC_HOURS); | ||
675 | day = CMOS_READ(RTC_DAY_OF_MONTH); | ||
676 | mon = CMOS_READ(RTC_MONTH); | ||
677 | year = CMOS_READ(RTC_YEAR); | ||
678 | } while (sec != CMOS_READ(RTC_SECONDS)); | ||
679 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { | ||
680 | BCD_TO_BIN(sec); | ||
681 | BCD_TO_BIN(min); | ||
682 | BCD_TO_BIN(hour); | ||
683 | BCD_TO_BIN(day); | ||
684 | BCD_TO_BIN(mon); | ||
685 | BCD_TO_BIN(year); | ||
686 | } | ||
687 | if ((year += 1900) < 1970) | ||
688 | year += 100; | ||
689 | } | ||
690 | |||
691 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | ||
692 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | ||
693 | set_normalized_timespec(&wall_to_monotonic, | ||
694 | -xtime.tv_sec, -xtime.tv_nsec); | ||
695 | |||
696 | if (mregs) { | ||
697 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
698 | tmp &= ~MSTK_CREG_READ; | ||
699 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
700 | |||
701 | spin_unlock_irq(&mostek_lock); | ||
702 | } | ||
703 | } | ||
704 | |||
705 | void __init clock_probe(void) | ||
706 | { | ||
707 | struct linux_prom_registers clk_reg[2]; | ||
708 | char model[128]; | ||
709 | int node, busnd = -1, err; | ||
710 | unsigned long flags; | ||
711 | struct linux_central *cbus; | ||
712 | #ifdef CONFIG_PCI | ||
713 | struct linux_ebus *ebus = NULL; | ||
714 | struct sparc_isa_bridge *isa_br = NULL; | ||
715 | #endif | ||
716 | static int invoked; | ||
717 | |||
718 | if (invoked) | ||
719 | return; | ||
720 | invoked = 1; | ||
721 | |||
722 | |||
723 | if (this_is_starfire) { | ||
724 | /* davem suggests we keep this within the 4M locked kernel image */ | ||
725 | static char obp_gettod[256]; | ||
726 | static u32 unix_tod; | ||
727 | |||
728 | sprintf(obp_gettod, "h# %08x unix-gettod", | ||
729 | (unsigned int) (long) &unix_tod); | ||
730 | prom_feval(obp_gettod); | ||
731 | xtime.tv_sec = unix_tod; | ||
732 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | ||
733 | set_normalized_timespec(&wall_to_monotonic, | ||
734 | -xtime.tv_sec, -xtime.tv_nsec); | ||
735 | return; | ||
736 | } | ||
737 | |||
738 | local_irq_save(flags); | ||
739 | |||
740 | cbus = central_bus; | ||
741 | if (cbus != NULL) | ||
742 | busnd = central_bus->child->prom_node; | ||
743 | |||
744 | /* Check FHC Central then EBUSs then ISA bridges then SBUSs. | ||
745 | * That way we handle the presence of multiple properly. | ||
746 | * | ||
747 | * As a special case, machines with Central must provide the | ||
748 | * timer chip there. | ||
749 | */ | ||
750 | #ifdef CONFIG_PCI | ||
751 | if (ebus_chain != NULL) { | ||
752 | ebus = ebus_chain; | ||
753 | if (busnd == -1) | ||
754 | busnd = ebus->prom_node; | ||
755 | } | ||
756 | if (isa_chain != NULL) { | ||
757 | isa_br = isa_chain; | ||
758 | if (busnd == -1) | ||
759 | busnd = isa_br->prom_node; | ||
760 | } | ||
761 | #endif | ||
762 | if (sbus_root != NULL && busnd == -1) | ||
763 | busnd = sbus_root->prom_node; | ||
764 | |||
765 | if (busnd == -1) { | ||
766 | prom_printf("clock_probe: problem, cannot find bus to search.\n"); | ||
767 | prom_halt(); | ||
768 | } | ||
769 | |||
770 | node = prom_getchild(busnd); | ||
771 | |||
772 | while (1) { | ||
773 | if (!node) | ||
774 | model[0] = 0; | ||
775 | else | ||
776 | prom_getstring(node, "model", model, sizeof(model)); | ||
777 | if (strcmp(model, "mk48t02") && | ||
778 | strcmp(model, "mk48t08") && | ||
779 | strcmp(model, "mk48t59") && | ||
780 | strcmp(model, "m5819") && | ||
781 | strcmp(model, "m5819p") && | ||
782 | strcmp(model, "m5823") && | ||
783 | strcmp(model, "ds1287")) { | ||
784 | if (cbus != NULL) { | ||
785 | prom_printf("clock_probe: Central bus lacks timer chip.\n"); | ||
786 | prom_halt(); | ||
787 | } | ||
788 | |||
789 | if (node != 0) | ||
790 | node = prom_getsibling(node); | ||
791 | #ifdef CONFIG_PCI | ||
792 | while ((node == 0) && ebus != NULL) { | ||
793 | ebus = ebus->next; | ||
794 | if (ebus != NULL) { | ||
795 | busnd = ebus->prom_node; | ||
796 | node = prom_getchild(busnd); | ||
797 | } | ||
798 | } | ||
799 | while ((node == 0) && isa_br != NULL) { | ||
800 | isa_br = isa_br->next; | ||
801 | if (isa_br != NULL) { | ||
802 | busnd = isa_br->prom_node; | ||
803 | node = prom_getchild(busnd); | ||
804 | } | ||
805 | } | ||
806 | #endif | ||
807 | if (node == 0) { | ||
808 | prom_printf("clock_probe: Cannot find timer chip\n"); | ||
809 | prom_halt(); | ||
810 | } | ||
811 | continue; | ||
812 | } | ||
813 | |||
814 | err = prom_getproperty(node, "reg", (char *)clk_reg, | ||
815 | sizeof(clk_reg)); | ||
816 | if(err == -1) { | ||
817 | prom_printf("clock_probe: Cannot get Mostek reg property\n"); | ||
818 | prom_halt(); | ||
819 | } | ||
820 | |||
821 | if (cbus != NULL) { | ||
822 | apply_fhc_ranges(central_bus->child, clk_reg, 1); | ||
823 | apply_central_ranges(central_bus, clk_reg, 1); | ||
824 | } | ||
825 | #ifdef CONFIG_PCI | ||
826 | else if (ebus != NULL) { | ||
827 | struct linux_ebus_device *edev; | ||
828 | |||
829 | for_each_ebusdev(edev, ebus) | ||
830 | if (edev->prom_node == node) | ||
831 | break; | ||
832 | if (edev == NULL) { | ||
833 | if (isa_chain != NULL) | ||
834 | goto try_isa_clock; | ||
835 | prom_printf("%s: Mostek not probed by EBUS\n", | ||
836 | __FUNCTION__); | ||
837 | prom_halt(); | ||
838 | } | ||
839 | |||
840 | if (!strcmp(model, "ds1287") || | ||
841 | !strcmp(model, "m5819") || | ||
842 | !strcmp(model, "m5819p") || | ||
843 | !strcmp(model, "m5823")) { | ||
844 | ds1287_regs = edev->resource[0].start; | ||
845 | } else { | ||
846 | mstk48t59_regs = edev->resource[0].start; | ||
847 | mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; | ||
848 | } | ||
849 | break; | ||
850 | } | ||
851 | else if (isa_br != NULL) { | ||
852 | struct sparc_isa_device *isadev; | ||
853 | |||
854 | try_isa_clock: | ||
855 | for_each_isadev(isadev, isa_br) | ||
856 | if (isadev->prom_node == node) | ||
857 | break; | ||
858 | if (isadev == NULL) { | ||
859 | prom_printf("%s: Mostek not probed by ISA\n"); | ||
860 | prom_halt(); | ||
861 | } | ||
862 | if (!strcmp(model, "ds1287") || | ||
863 | !strcmp(model, "m5819") || | ||
864 | !strcmp(model, "m5819p") || | ||
865 | !strcmp(model, "m5823")) { | ||
866 | ds1287_regs = isadev->resource.start; | ||
867 | } else { | ||
868 | mstk48t59_regs = isadev->resource.start; | ||
869 | mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; | ||
870 | } | ||
871 | break; | ||
872 | } | ||
873 | #endif | ||
874 | else { | ||
875 | if (sbus_root->num_sbus_ranges) { | ||
876 | int nranges = sbus_root->num_sbus_ranges; | ||
877 | int rngc; | ||
878 | |||
879 | for (rngc = 0; rngc < nranges; rngc++) | ||
880 | if (clk_reg[0].which_io == | ||
881 | sbus_root->sbus_ranges[rngc].ot_child_space) | ||
882 | break; | ||
883 | if (rngc == nranges) { | ||
884 | prom_printf("clock_probe: Cannot find ranges for " | ||
885 | "clock regs.\n"); | ||
886 | prom_halt(); | ||
887 | } | ||
888 | clk_reg[0].which_io = | ||
889 | sbus_root->sbus_ranges[rngc].ot_parent_space; | ||
890 | clk_reg[0].phys_addr += | ||
891 | sbus_root->sbus_ranges[rngc].ot_parent_base; | ||
892 | } | ||
893 | } | ||
894 | |||
895 | if(model[5] == '0' && model[6] == '2') { | ||
896 | mstk48t02_regs = (((u64)clk_reg[0].phys_addr) | | ||
897 | (((u64)clk_reg[0].which_io)<<32UL)); | ||
898 | } else if(model[5] == '0' && model[6] == '8') { | ||
899 | mstk48t08_regs = (((u64)clk_reg[0].phys_addr) | | ||
900 | (((u64)clk_reg[0].which_io)<<32UL)); | ||
901 | mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02; | ||
902 | } else { | ||
903 | mstk48t59_regs = (((u64)clk_reg[0].phys_addr) | | ||
904 | (((u64)clk_reg[0].which_io)<<32UL)); | ||
905 | mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; | ||
906 | } | ||
907 | break; | ||
908 | } | ||
909 | |||
910 | if (mstk48t02_regs != 0UL) { | ||
911 | /* Report a low battery voltage condition. */ | ||
912 | if (has_low_battery()) | ||
913 | prom_printf("NVRAM: Low battery voltage!\n"); | ||
914 | |||
915 | /* Kick start the clock if it is completely stopped. */ | ||
916 | if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) | ||
917 | kick_start_clock(); | ||
918 | } | ||
919 | |||
920 | set_system_time(); | ||
921 | |||
922 | local_irq_restore(flags); | ||
923 | } | ||
924 | |||
925 | /* This is gets the master TICK_INT timer going. */ | ||
926 | static unsigned long sparc64_init_timers(void) | ||
927 | { | ||
928 | unsigned long clock; | ||
929 | int node; | ||
930 | #ifdef CONFIG_SMP | ||
931 | extern void smp_tick_init(void); | ||
932 | #endif | ||
933 | |||
934 | if (tlb_type == spitfire) { | ||
935 | unsigned long ver, manuf, impl; | ||
936 | |||
937 | __asm__ __volatile__ ("rdpr %%ver, %0" | ||
938 | : "=&r" (ver)); | ||
939 | manuf = ((ver >> 48) & 0xffff); | ||
940 | impl = ((ver >> 32) & 0xffff); | ||
941 | if (manuf == 0x17 && impl == 0x13) { | ||
942 | /* Hummingbird, aka Ultra-IIe */ | ||
943 | tick_ops = &hbtick_operations; | ||
944 | node = prom_root_node; | ||
945 | clock = prom_getint(node, "stick-frequency"); | ||
946 | } else { | ||
947 | tick_ops = &tick_operations; | ||
948 | cpu_find_by_instance(0, &node, NULL); | ||
949 | clock = prom_getint(node, "clock-frequency"); | ||
950 | } | ||
951 | } else { | ||
952 | tick_ops = &stick_operations; | ||
953 | node = prom_root_node; | ||
954 | clock = prom_getint(node, "stick-frequency"); | ||
955 | } | ||
956 | timer_tick_offset = clock / HZ; | ||
957 | |||
958 | #ifdef CONFIG_SMP | ||
959 | smp_tick_init(); | ||
960 | #endif | ||
961 | |||
962 | return clock; | ||
963 | } | ||
964 | |||
965 | static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_regs *)) | ||
966 | { | ||
967 | unsigned long pstate; | ||
968 | int err; | ||
969 | |||
970 | /* Register IRQ handler. */ | ||
971 | err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, SA_STATIC_ALLOC, | ||
972 | "timer", NULL); | ||
973 | |||
974 | if (err) { | ||
975 | prom_printf("Serious problem, cannot register TICK_INT\n"); | ||
976 | prom_halt(); | ||
977 | } | ||
978 | |||
979 | /* Guarantee that the following sequences execute | ||
980 | * uninterrupted. | ||
981 | */ | ||
982 | __asm__ __volatile__("rdpr %%pstate, %0\n\t" | ||
983 | "wrpr %0, %1, %%pstate" | ||
984 | : "=r" (pstate) | ||
985 | : "i" (PSTATE_IE)); | ||
986 | |||
987 | tick_ops->init_tick(timer_tick_offset); | ||
988 | |||
989 | /* Restore PSTATE_IE. */ | ||
990 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" | ||
991 | : /* no outputs */ | ||
992 | : "r" (pstate)); | ||
993 | |||
994 | local_irq_enable(); | ||
995 | } | ||
996 | |||
997 | struct freq_table { | ||
998 | unsigned long udelay_val_ref; | ||
999 | unsigned long clock_tick_ref; | ||
1000 | unsigned int ref_freq; | ||
1001 | }; | ||
1002 | static DEFINE_PER_CPU(struct freq_table, sparc64_freq_table) = { 0, 0, 0 }; | ||
1003 | |||
1004 | unsigned long sparc64_get_clock_tick(unsigned int cpu) | ||
1005 | { | ||
1006 | struct freq_table *ft = &per_cpu(sparc64_freq_table, cpu); | ||
1007 | |||
1008 | if (ft->clock_tick_ref) | ||
1009 | return ft->clock_tick_ref; | ||
1010 | return cpu_data(cpu).clock_tick; | ||
1011 | } | ||
1012 | |||
1013 | #ifdef CONFIG_CPU_FREQ | ||
1014 | |||
1015 | static int sparc64_cpufreq_notifier(struct notifier_block *nb, unsigned long val, | ||
1016 | void *data) | ||
1017 | { | ||
1018 | struct cpufreq_freqs *freq = data; | ||
1019 | unsigned int cpu = freq->cpu; | ||
1020 | struct freq_table *ft = &per_cpu(sparc64_freq_table, cpu); | ||
1021 | |||
1022 | if (!ft->ref_freq) { | ||
1023 | ft->ref_freq = freq->old; | ||
1024 | ft->udelay_val_ref = cpu_data(cpu).udelay_val; | ||
1025 | ft->clock_tick_ref = cpu_data(cpu).clock_tick; | ||
1026 | } | ||
1027 | if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || | ||
1028 | (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || | ||
1029 | (val == CPUFREQ_RESUMECHANGE)) { | ||
1030 | cpu_data(cpu).udelay_val = | ||
1031 | cpufreq_scale(ft->udelay_val_ref, | ||
1032 | ft->ref_freq, | ||
1033 | freq->new); | ||
1034 | cpu_data(cpu).clock_tick = | ||
1035 | cpufreq_scale(ft->clock_tick_ref, | ||
1036 | ft->ref_freq, | ||
1037 | freq->new); | ||
1038 | } | ||
1039 | |||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | static struct notifier_block sparc64_cpufreq_notifier_block = { | ||
1044 | .notifier_call = sparc64_cpufreq_notifier | ||
1045 | }; | ||
1046 | |||
1047 | #endif /* CONFIG_CPU_FREQ */ | ||
1048 | |||
1049 | static struct time_interpolator sparc64_cpu_interpolator = { | ||
1050 | .source = TIME_SOURCE_CPU, | ||
1051 | .shift = 16, | ||
1052 | .mask = 0xffffffffffffffffLL | ||
1053 | }; | ||
1054 | |||
1055 | /* The quotient formula is taken from the IA64 port. */ | ||
1056 | #define SPARC64_NSEC_PER_CYC_SHIFT 30UL | ||
1057 | void __init time_init(void) | ||
1058 | { | ||
1059 | unsigned long clock = sparc64_init_timers(); | ||
1060 | |||
1061 | sparc64_cpu_interpolator.frequency = clock; | ||
1062 | register_time_interpolator(&sparc64_cpu_interpolator); | ||
1063 | |||
1064 | /* Now that the interpolator is registered, it is | ||
1065 | * safe to start the timer ticking. | ||
1066 | */ | ||
1067 | sparc64_start_timers(timer_interrupt); | ||
1068 | |||
1069 | timer_ticks_per_nsec_quotient = | ||
1070 | (((NSEC_PER_SEC << SPARC64_NSEC_PER_CYC_SHIFT) + | ||
1071 | (clock / 2)) / clock); | ||
1072 | |||
1073 | #ifdef CONFIG_CPU_FREQ | ||
1074 | cpufreq_register_notifier(&sparc64_cpufreq_notifier_block, | ||
1075 | CPUFREQ_TRANSITION_NOTIFIER); | ||
1076 | #endif | ||
1077 | } | ||
1078 | |||
1079 | unsigned long long sched_clock(void) | ||
1080 | { | ||
1081 | unsigned long ticks = tick_ops->get_tick(); | ||
1082 | |||
1083 | return (ticks * timer_ticks_per_nsec_quotient) | ||
1084 | >> SPARC64_NSEC_PER_CYC_SHIFT; | ||
1085 | } | ||
1086 | |||
1087 | static int set_rtc_mmss(unsigned long nowtime) | ||
1088 | { | ||
1089 | int real_seconds, real_minutes, chip_minutes; | ||
1090 | unsigned long mregs = mstk48t02_regs; | ||
1091 | #ifdef CONFIG_PCI | ||
1092 | unsigned long dregs = ds1287_regs; | ||
1093 | #else | ||
1094 | unsigned long dregs = 0UL; | ||
1095 | #endif | ||
1096 | unsigned long flags; | ||
1097 | u8 tmp; | ||
1098 | |||
1099 | /* | ||
1100 | * Not having a register set can lead to trouble. | ||
1101 | * Also starfire doesn't have a tod clock. | ||
1102 | */ | ||
1103 | if (!mregs && !dregs) | ||
1104 | return -1; | ||
1105 | |||
1106 | if (mregs) { | ||
1107 | spin_lock_irqsave(&mostek_lock, flags); | ||
1108 | |||
1109 | /* Read the current RTC minutes. */ | ||
1110 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
1111 | tmp |= MSTK_CREG_READ; | ||
1112 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
1113 | |||
1114 | chip_minutes = MSTK_REG_MIN(mregs); | ||
1115 | |||
1116 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
1117 | tmp &= ~MSTK_CREG_READ; | ||
1118 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
1119 | |||
1120 | /* | ||
1121 | * since we're only adjusting minutes and seconds, | ||
1122 | * don't interfere with hour overflow. This avoids | ||
1123 | * messing with unknown time zones but requires your | ||
1124 | * RTC not to be off by more than 15 minutes | ||
1125 | */ | ||
1126 | real_seconds = nowtime % 60; | ||
1127 | real_minutes = nowtime / 60; | ||
1128 | if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) | ||
1129 | real_minutes += 30; /* correct for half hour time zone */ | ||
1130 | real_minutes %= 60; | ||
1131 | |||
1132 | if (abs(real_minutes - chip_minutes) < 30) { | ||
1133 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
1134 | tmp |= MSTK_CREG_WRITE; | ||
1135 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
1136 | |||
1137 | MSTK_SET_REG_SEC(mregs,real_seconds); | ||
1138 | MSTK_SET_REG_MIN(mregs,real_minutes); | ||
1139 | |||
1140 | tmp = mostek_read(mregs + MOSTEK_CREG); | ||
1141 | tmp &= ~MSTK_CREG_WRITE; | ||
1142 | mostek_write(mregs + MOSTEK_CREG, tmp); | ||
1143 | |||
1144 | spin_unlock_irqrestore(&mostek_lock, flags); | ||
1145 | |||
1146 | return 0; | ||
1147 | } else { | ||
1148 | spin_unlock_irqrestore(&mostek_lock, flags); | ||
1149 | |||
1150 | return -1; | ||
1151 | } | ||
1152 | } else { | ||
1153 | int retval = 0; | ||
1154 | unsigned char save_control, save_freq_select; | ||
1155 | |||
1156 | /* Stolen from arch/i386/kernel/time.c, see there for | ||
1157 | * credits and descriptive comments. | ||
1158 | */ | ||
1159 | spin_lock_irqsave(&rtc_lock, flags); | ||
1160 | save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ | ||
1161 | CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); | ||
1162 | |||
1163 | save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ | ||
1164 | CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); | ||
1165 | |||
1166 | chip_minutes = CMOS_READ(RTC_MINUTES); | ||
1167 | if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) | ||
1168 | BCD_TO_BIN(chip_minutes); | ||
1169 | real_seconds = nowtime % 60; | ||
1170 | real_minutes = nowtime / 60; | ||
1171 | if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) | ||
1172 | real_minutes += 30; | ||
1173 | real_minutes %= 60; | ||
1174 | |||
1175 | if (abs(real_minutes - chip_minutes) < 30) { | ||
1176 | if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { | ||
1177 | BIN_TO_BCD(real_seconds); | ||
1178 | BIN_TO_BCD(real_minutes); | ||
1179 | } | ||
1180 | CMOS_WRITE(real_seconds,RTC_SECONDS); | ||
1181 | CMOS_WRITE(real_minutes,RTC_MINUTES); | ||
1182 | } else { | ||
1183 | printk(KERN_WARNING | ||
1184 | "set_rtc_mmss: can't update from %d to %d\n", | ||
1185 | chip_minutes, real_minutes); | ||
1186 | retval = -1; | ||
1187 | } | ||
1188 | |||
1189 | CMOS_WRITE(save_control, RTC_CONTROL); | ||
1190 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); | ||
1191 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1192 | |||
1193 | return retval; | ||
1194 | } | ||
1195 | } | ||