1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Carsten Langgaard, carstenl@mips.com 4 * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 5 * 6 * Setting up the clock on the MIPS boards. 7 */ 8 #include <linux/types.h> 9 #include <linux/i8253.h> 10 #include <linux/init.h> 11 #include <linux/kernel_stat.h> 12 #include <linux/libfdt.h> 13 #include <linux/math64.h> 14 #include <linux/sched.h> 15 #include <linux/spinlock.h> 16 #include <linux/interrupt.h> 17 #include <linux/timex.h> 18 #include <linux/mc146818rtc.h> 19 20 #include <asm/cpu.h> 21 #include <asm/mipsregs.h> 22 #include <asm/mipsmtregs.h> 23 #include <asm/hardirq.h> 24 #include <asm/irq.h> 25 #include <asm/div64.h> 26 #include <asm/setup.h> 27 #include <asm/time.h> 28 #include <asm/mc146818-time.h> 29 #include <asm/msc01_ic.h> 30 #include <asm/mips-cps.h> 31 32 #include <asm/mips-boards/generic.h> 33 #include <asm/mips-boards/maltaint.h> 34 35 static int mips_cpu_timer_irq; 36 static int mips_cpu_perf_irq; 37 extern int cp0_perfcount_irq; 38 39 static unsigned int gic_frequency; 40 41 static void mips_timer_dispatch(void) 42 { 43 do_IRQ(mips_cpu_timer_irq); 44 } 45 46 static void mips_perf_dispatch(void) 47 { 48 do_IRQ(mips_cpu_perf_irq); 49 } 50 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 58 /* 59 * Estimate CPU and GIC frequencies. 60 */ 61 static void __init estimate_frequencies(void) 62 { 63 unsigned long flags; 64 unsigned int count, start; 65 unsigned char secs1, secs2, ctrl; 66 int secs; 67 u64 giccount = 0, gicstart = 0; 68 69 local_irq_save(flags); 70 71 if (mips_gic_present()) 72 clear_gic_config(GIC_CONFIG_COUNTSTOP); 73 74 /* 75 * Read counters exactly on rising edge of update flag. 76 * This helps get an accurate reading under virtualisation. 77 */ 78 while (CMOS_READ(RTC_REG_A) & RTC_UIP); 79 while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); 80 start = read_c0_count(); 81 if (mips_gic_present()) 82 gicstart = read_gic_counter(); 83 84 /* Wait for falling edge before reading RTC. */ 85 while (CMOS_READ(RTC_REG_A) & RTC_UIP); 86 secs1 = CMOS_READ(RTC_SECONDS); 87 88 /* Read counters again exactly on rising edge of update flag. */ 89 while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); 90 count = read_c0_count(); 91 if (mips_gic_present()) 92 giccount = read_gic_counter(); 93 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 100 local_irq_restore(flags); 101 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 110 count -= start; 111 count /= secs; 112 mips_hpt_frequency = count; 113 114 if (mips_gic_present()) { 115 giccount = div_u64(giccount - gicstart, secs); 116 gic_frequency = giccount; 117 } 118 } 119 120 void read_persistent_clock64(struct timespec64 *ts) 121 { 122 ts->tv_sec = mc146818_get_cmos_time(); 123 ts->tv_nsec = 0; 124 } 125 126 int get_c0_fdc_int(void) 127 { 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; 136 } 137 138 if (cpu_has_veic) 139 return -1; 140 else if (mips_gic_present()) 141 return gic_get_c0_fdc_int(); 142 else if (cp0_fdc_irq >= 0) 143 return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; 144 else 145 return -1; 146 } 147 148 int get_c0_perfcount_int(void) 149 { 150 if (cpu_has_veic) { 151 set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch); 152 mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; 153 } else if (mips_gic_present()) { 154 mips_cpu_perf_irq = gic_get_c0_perfcount_int(); 155 } else if (cp0_perfcount_irq >= 0) { 156 mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; 157 } else { 158 mips_cpu_perf_irq = -1; 159 } 160 161 return mips_cpu_perf_irq; 162 } 163 EXPORT_SYMBOL_GPL(get_c0_perfcount_int); 164 165 unsigned int get_c0_compare_int(void) 166 { 167 if (cpu_has_veic) { 168 set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch); 169 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; 170 } else if (mips_gic_present()) { 171 mips_cpu_timer_irq = gic_get_c0_compare_int(); 172 } else { 173 mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; 174 } 175 176 return mips_cpu_timer_irq; 177 } 178 179 static void __init init_rtc(void) 180 { 181 unsigned char freq, ctrl; 182 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); 187 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); 192 } 193 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"); 217 218 of_node_put(node); 219 } 220 221 #endif 222 223 void __init plat_time_init(void) 224 { 225 unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK); 226 unsigned int freq; 227 228 init_rtc(); 229 estimate_frequencies(); 230 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); 236 printk("CPU frequency %d.%02d MHz\n", freq/1000000, 237 (freq%1000000)*100/1000000); 238 239 #ifdef CONFIG_I8253 240 /* Only Malta has a PIT. */ 241 setup_pit_timer(); 242 #endif 243 244 if (mips_gic_present()) { 245 freq = freqround(gic_frequency, 5000); 246 printk("GIC frequency %d.%02d MHz\n", freq/1000000, 247 (freq%1000000)*100/1000000); 248 #ifdef CONFIG_CLKSRC_MIPS_GIC 249 update_gic_frequency_dt(); 250 timer_probe(); 251 #endif 252 } 253 } 254
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.