diff options
Diffstat (limited to 'arch/mips/sgi-ip22/ip22-time.c')
-rw-r--r-- | arch/mips/sgi-ip22/ip22-time.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c new file mode 100644 index 000000000..045aa89f2 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-time.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Time operations for IP22 machines. Original code may come from | ||
7 | * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure) | ||
8 | * | ||
9 | * Copyright (C) 2001 by Ladislav Michl | ||
10 | * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org) | ||
11 | */ | ||
12 | #include <linux/bcd.h> | ||
13 | #include <linux/i8253.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel_stat.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/ftrace.h> | ||
21 | |||
22 | #include <asm/cpu.h> | ||
23 | #include <asm/mipsregs.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq.h> | ||
26 | #include <asm/time.h> | ||
27 | #include <asm/sgialib.h> | ||
28 | #include <asm/sgi/ioc.h> | ||
29 | #include <asm/sgi/hpc3.h> | ||
30 | #include <asm/sgi/ip22.h> | ||
31 | |||
32 | static unsigned long dosample(void) | ||
33 | { | ||
34 | u32 ct0, ct1; | ||
35 | u8 msb; | ||
36 | |||
37 | /* Start the counter. */ | ||
38 | sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | | ||
39 | SGINT_TCWORD_MRGEN); | ||
40 | sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff; | ||
41 | sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8; | ||
42 | |||
43 | /* Get initial counter invariant */ | ||
44 | ct0 = read_c0_count(); | ||
45 | |||
46 | /* Latch and spin until top byte of counter2 is zero */ | ||
47 | do { | ||
48 | writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword); | ||
49 | (void) readb(&sgint->tcnt2); | ||
50 | msb = readb(&sgint->tcnt2); | ||
51 | ct1 = read_c0_count(); | ||
52 | } while (msb); | ||
53 | |||
54 | /* Stop the counter. */ | ||
55 | writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST, | ||
56 | &sgint->tcword); | ||
57 | /* | ||
58 | * Return the difference, this is how far the r4k counter increments | ||
59 | * for every 1/HZ seconds. We round off the nearest 1 MHz of master | ||
60 | * clock (= 1000000 / HZ / 2). | ||
61 | */ | ||
62 | |||
63 | return (ct1 - ct0) / (500000/HZ) * (500000/HZ); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Here we need to calibrate the cycle counter to at least be close. | ||
68 | */ | ||
69 | __init void plat_time_init(void) | ||
70 | { | ||
71 | unsigned long r4k_ticks[3]; | ||
72 | unsigned long r4k_tick; | ||
73 | |||
74 | /* | ||
75 | * Figure out the r4k offset, the algorithm is very simple and works in | ||
76 | * _all_ cases as long as the 8254 counter register itself works ok (as | ||
77 | * an interrupt driving timer it does not because of bug, this is why | ||
78 | * we are using the onchip r4k counter/compare register to serve this | ||
79 | * purpose, but for r4k_offset calculation it will work ok for us). | ||
80 | * There are other very complicated ways of performing this calculation | ||
81 | * but this one works just fine so I am not going to futz around. ;-) | ||
82 | */ | ||
83 | printk(KERN_INFO "Calibrating system timer... "); | ||
84 | dosample(); /* Prime cache. */ | ||
85 | dosample(); /* Prime cache. */ | ||
86 | /* Zero is NOT an option. */ | ||
87 | do { | ||
88 | r4k_ticks[0] = dosample(); | ||
89 | } while (!r4k_ticks[0]); | ||
90 | do { | ||
91 | r4k_ticks[1] = dosample(); | ||
92 | } while (!r4k_ticks[1]); | ||
93 | |||
94 | if (r4k_ticks[0] != r4k_ticks[1]) { | ||
95 | printk("warning: timer counts differ, retrying... "); | ||
96 | r4k_ticks[2] = dosample(); | ||
97 | if (r4k_ticks[2] == r4k_ticks[0] | ||
98 | || r4k_ticks[2] == r4k_ticks[1]) | ||
99 | r4k_tick = r4k_ticks[2]; | ||
100 | else { | ||
101 | printk("disagreement, using average... "); | ||
102 | r4k_tick = (r4k_ticks[0] + r4k_ticks[1] | ||
103 | + r4k_ticks[2]) / 3; | ||
104 | } | ||
105 | } else | ||
106 | r4k_tick = r4k_ticks[0]; | ||
107 | |||
108 | printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick, | ||
109 | (int) (r4k_tick / (500000 / HZ)), | ||
110 | (int) (r4k_tick % (500000 / HZ))); | ||
111 | |||
112 | mips_hpt_frequency = r4k_tick * HZ; | ||
113 | |||
114 | if (ip22_is_fullhouse()) | ||
115 | setup_pit_timer(); | ||
116 | } | ||
117 | |||
118 | /* Generic SGI handler for (spurious) 8254 interrupts */ | ||
119 | void __irq_entry indy_8254timer_irq(void) | ||
120 | { | ||
121 | int irq = SGI_8254_0_IRQ; | ||
122 | ULONG cnt; | ||
123 | char c; | ||
124 | |||
125 | irq_enter(); | ||
126 | kstat_incr_irq_this_cpu(irq); | ||
127 | printk(KERN_ALERT "Oops, got 8254 interrupt.\n"); | ||
128 | ArcRead(0, &c, 1, &cnt); | ||
129 | ArcEnterInteractiveMode(); | ||
130 | irq_exit(); | ||
131 | } | ||