Commit | Line | Data |
---|---|---|
41173abc | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * Carsten Langgaard, carstenl@mips.com | |
4 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | |
5 | * | |
1da177e4 LT |
6 | * Setting up the clock on the MIPS boards. |
7 | */ | |
1da177e4 | 8 | #include <linux/types.h> |
334955ef | 9 | #include <linux/i8253.h> |
1da177e4 LT |
10 | #include <linux/init.h> |
11 | #include <linux/kernel_stat.h> | |
4287adec | 12 | #include <linux/libfdt.h> |
24e1df66 | 13 | #include <linux/math64.h> |
1da177e4 LT |
14 | #include <linux/sched.h> |
15 | #include <linux/spinlock.h> | |
16 | #include <linux/interrupt.h> | |
1da177e4 LT |
17 | #include <linux/timex.h> |
18 | #include <linux/mc146818rtc.h> | |
19 | ||
8ff374b9 | 20 | #include <asm/cpu.h> |
1da177e4 | 21 | #include <asm/mipsregs.h> |
41c594ab | 22 | #include <asm/mipsmtregs.h> |
e01402b1 RB |
23 | #include <asm/hardirq.h> |
24 | #include <asm/irq.h> | |
1da177e4 | 25 | #include <asm/div64.h> |
b81947c6 | 26 | #include <asm/setup.h> |
1da177e4 LT |
27 | #include <asm/time.h> |
28 | #include <asm/mc146818-time.h> | |
e01402b1 | 29 | #include <asm/msc01_ic.h> |
72eb2995 | 30 | #include <asm/mips-cps.h> |
1da177e4 LT |
31 | |
32 | #include <asm/mips-boards/generic.h> | |
e01402b1 | 33 | #include <asm/mips-boards/maltaint.h> |
1da177e4 | 34 | |
e01402b1 | 35 | static int mips_cpu_timer_irq; |
39b8d525 | 36 | static int mips_cpu_perf_irq; |
3b1d4ed5 | 37 | extern int cp0_perfcount_irq; |
1da177e4 | 38 | |
b0854514 AB |
39 | static unsigned int gic_frequency; |
40 | ||
937a8015 | 41 | static void mips_timer_dispatch(void) |
1da177e4 | 42 | { |
937a8015 | 43 | do_IRQ(mips_cpu_timer_irq); |
e01402b1 RB |
44 | } |
45 | ||
ffe9ee47 CD |
46 | static void mips_perf_dispatch(void) |
47 | { | |
39b8d525 | 48 | do_IRQ(mips_cpu_perf_irq); |
ffe9ee47 CD |
49 | } |
50 | ||
778eeb1b SH |
51 | static unsigned int freqround(unsigned int freq, unsigned int amount) |
52 | { | |
53 | freq += amount; | |
54 | freq -= freq % (amount*2); | |
55 | return freq; | |
56 | } | |
57 | ||
1da177e4 | 58 | /* |
778eeb1b | 59 | * Estimate CPU and GIC frequencies. |
1da177e4 | 60 | */ |
778eeb1b | 61 | static void __init estimate_frequencies(void) |
1da177e4 | 62 | { |
e79f55a8 | 63 | unsigned long flags; |
778eeb1b | 64 | unsigned int count, start; |
24e1df66 JH |
65 | unsigned char secs1, secs2, ctrl; |
66 | int secs; | |
a5a1d1c2 | 67 | u64 giccount = 0, gicstart = 0; |
1da177e4 LT |
68 | |
69 | local_irq_save(flags); | |
70 | ||
72eb2995 | 71 | if (mips_gic_present()) |
60702867 | 72 | clear_gic_config(GIC_CONFIG_COUNTSTOP); |
aab4673b | 73 | |
24e1df66 JH |
74 | /* |
75 | * Read counters exactly on rising edge of update flag. | |
76 | * This helps get an accurate reading under virtualisation. | |
77 | */ | |
1da177e4 LT |
78 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); |
79 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | |
70e46f48 | 80 | start = read_c0_count(); |
72eb2995 | 81 | if (mips_gic_present()) |
60702867 | 82 | gicstart = read_gic_counter(); |
1da177e4 | 83 | |
24e1df66 | 84 | /* Wait for falling edge before reading RTC. */ |
1da177e4 | 85 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); |
24e1df66 | 86 | secs1 = CMOS_READ(RTC_SECONDS); |
1da177e4 | 87 | |
24e1df66 JH |
88 | /* Read counters again exactly on rising edge of update flag. */ |
89 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | |
778eeb1b | 90 | count = read_c0_count(); |
72eb2995 | 91 | if (mips_gic_present()) |
60702867 | 92 | giccount = read_gic_counter(); |
1da177e4 | 93 | |
24e1df66 JH |
94 | /* Wait for falling edge before reading RTC again. */ |
95 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); | |
96 | secs2 = CMOS_READ(RTC_SECONDS); | |
97 | ||
98 | ctrl = CMOS_READ(RTC_CONTROL); | |
99 | ||
1da177e4 | 100 | local_irq_restore(flags); |
1da177e4 | 101 | |
24e1df66 JH |
102 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { |
103 | secs1 = bcd2bin(secs1); | |
104 | secs2 = bcd2bin(secs2); | |
105 | } | |
106 | secs = secs2 - secs1; | |
107 | if (secs < 1) | |
108 | secs += 60; | |
109 | ||
778eeb1b | 110 | count -= start; |
24e1df66 | 111 | count /= secs; |
778eeb1b | 112 | mips_hpt_frequency = count; |
dfa762e1 | 113 | |
72eb2995 | 114 | if (mips_gic_present()) { |
24e1df66 | 115 | giccount = div_u64(giccount - gicstart, secs); |
778eeb1b | 116 | gic_frequency = giccount; |
dfa762e1 | 117 | } |
1da177e4 LT |
118 | } |
119 | ||
09adad17 | 120 | void read_persistent_clock64(struct timespec64 *ts) |
1da177e4 | 121 | { |
d4f587c6 MS |
122 | ts->tv_sec = mc146818_get_cmos_time(); |
123 | ts->tv_nsec = 0; | |
1da177e4 LT |
124 | } |
125 | ||
602e8a34 JH |
126 | int get_c0_fdc_int(void) |
127 | { | |
6249ecbb JH |
128 | /* |
129 | * Some cores claim the FDC is routable through the GIC, but it doesn't | |
130 | * actually seem to be connected for those Malta bitstreams. | |
131 | */ | |
132 | switch (current_cpu_type()) { | |
133 | case CPU_INTERAPTIV: | |
134 | case CPU_PROAPTIV: | |
135 | return -1; | |
3235c5f0 | 136 | } |
602e8a34 JH |
137 | |
138 | if (cpu_has_veic) | |
6249ecbb | 139 | return -1; |
72eb2995 | 140 | else if (mips_gic_present()) |
6249ecbb | 141 | return gic_get_c0_fdc_int(); |
602e8a34 | 142 | else if (cp0_fdc_irq >= 0) |
6249ecbb | 143 | return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; |
602e8a34 | 144 | else |
6249ecbb | 145 | return -1; |
602e8a34 JH |
146 | } |
147 | ||
a669efc4 | 148 | int get_c0_perfcount_int(void) |
ffe9ee47 | 149 | { |
e01402b1 | 150 | if (cpu_has_veic) { |
49a89efb | 151 | set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch); |
39b8d525 | 152 | mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; |
72eb2995 | 153 | } else if (mips_gic_present()) { |
e9de688d | 154 | mips_cpu_perf_irq = gic_get_c0_perfcount_int(); |
a669efc4 | 155 | } else if (cp0_perfcount_irq >= 0) { |
39b8d525 | 156 | mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; |
a669efc4 AB |
157 | } else { |
158 | mips_cpu_perf_irq = -1; | |
e01402b1 | 159 | } |
a669efc4 AB |
160 | |
161 | return mips_cpu_perf_irq; | |
ffe9ee47 | 162 | } |
0cb0985f | 163 | EXPORT_SYMBOL_GPL(get_c0_perfcount_int); |
e01402b1 | 164 | |
078a55fc | 165 | unsigned int get_c0_compare_int(void) |
ffe9ee47 | 166 | { |
ffe9ee47 | 167 | if (cpu_has_veic) { |
49a89efb | 168 | set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch); |
ffe9ee47 | 169 | mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; |
72eb2995 | 170 | } else if (mips_gic_present()) { |
e9de688d AB |
171 | mips_cpu_timer_irq = gic_get_c0_compare_int(); |
172 | } else { | |
3b1d4ed5 | 173 | mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; |
ffe9ee47 | 174 | } |
e01402b1 | 175 | |
38760d40 RB |
176 | return mips_cpu_timer_irq; |
177 | } | |
178 | ||
a87ea88d PB |
179 | static void __init init_rtc(void) |
180 | { | |
106eccb4 | 181 | unsigned char freq, ctrl; |
a87ea88d | 182 | |
106eccb4 JH |
183 | /* Set 32KHz time base if not already set */ |
184 | freq = CMOS_READ(RTC_FREQ_SELECT); | |
185 | if ((freq & RTC_DIV_CTL) != RTC_REF_CLCK_32KHZ) | |
186 | CMOS_WRITE(RTC_REF_CLCK_32KHZ, RTC_FREQ_SELECT); | |
a87ea88d | 187 | |
106eccb4 JH |
188 | /* Ensure SET bit is clear so RTC can run */ |
189 | ctrl = CMOS_READ(RTC_CONTROL); | |
190 | if (ctrl & RTC_SET) | |
191 | CMOS_WRITE(ctrl & ~RTC_SET, RTC_CONTROL); | |
a87ea88d PB |
192 | } |
193 | ||
4287adec MR |
194 | #ifdef CONFIG_CLKSRC_MIPS_GIC |
195 | static u32 gic_frequency_dt; | |
196 | ||
197 | static struct property gic_frequency_prop = { | |
198 | .name = "clock-frequency", | |
199 | .length = sizeof(u32), | |
200 | .value = &gic_frequency_dt, | |
201 | }; | |
202 | ||
203 | static void update_gic_frequency_dt(void) | |
204 | { | |
205 | struct device_node *node; | |
206 | ||
207 | gic_frequency_dt = cpu_to_be32(gic_frequency); | |
208 | ||
209 | node = of_find_compatible_node(NULL, NULL, "mti,gic-timer"); | |
210 | if (!node) { | |
211 | pr_err("mti,gic-timer device node not found\n"); | |
212 | return; | |
213 | } | |
214 | ||
215 | if (of_update_property(node, &gic_frequency_prop) < 0) | |
216 | pr_err("error updating gic frequency property\n"); | |
608d94cb LH |
217 | |
218 | of_node_put(node); | |
4287adec MR |
219 | } |
220 | ||
221 | #endif | |
222 | ||
38760d40 RB |
223 | void __init plat_time_init(void) |
224 | { | |
8ff374b9 | 225 | unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK); |
778eeb1b | 226 | unsigned int freq; |
38760d40 | 227 | |
a87ea88d | 228 | init_rtc(); |
778eeb1b | 229 | estimate_frequencies(); |
38760d40 | 230 | |
778eeb1b SH |
231 | freq = mips_hpt_frequency; |
232 | if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && | |
233 | (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) | |
234 | freq *= 2; | |
235 | freq = freqround(freq, 5000); | |
dfa762e1 | 236 | printk("CPU frequency %d.%02d MHz\n", freq/1000000, |
778eeb1b | 237 | (freq%1000000)*100/1000000); |
778eeb1b | 238 | |
778eeb1b SH |
239 | #ifdef CONFIG_I8253 |
240 | /* Only Malta has a PIT. */ | |
38760d40 | 241 | setup_pit_timer(); |
340ee4b9 | 242 | #endif |
ffe9ee47 | 243 | |
72eb2995 | 244 | if (mips_gic_present()) { |
dfa762e1 SH |
245 | freq = freqround(gic_frequency, 5000); |
246 | printk("GIC frequency %d.%02d MHz\n", freq/1000000, | |
247 | (freq%1000000)*100/1000000); | |
fa5635a2 | 248 | #ifdef CONFIG_CLKSRC_MIPS_GIC |
4287adec | 249 | update_gic_frequency_dt(); |
ba5d08c0 | 250 | timer_probe(); |
dfa762e1 SH |
251 | #endif |
252 | } | |
1da177e4 | 253 | } |