18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com 48c2ecf20Sopenharmony_ci * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Setting up the clock on the MIPS boards. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/i8253.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 128c2ecf20Sopenharmony_ci#include <linux/libfdt.h> 138c2ecf20Sopenharmony_ci#include <linux/math64.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/timex.h> 188c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/cpu.h> 218c2ecf20Sopenharmony_ci#include <asm/mipsregs.h> 228c2ecf20Sopenharmony_ci#include <asm/mipsmtregs.h> 238c2ecf20Sopenharmony_ci#include <asm/hardirq.h> 248c2ecf20Sopenharmony_ci#include <asm/irq.h> 258c2ecf20Sopenharmony_ci#include <asm/div64.h> 268c2ecf20Sopenharmony_ci#include <asm/setup.h> 278c2ecf20Sopenharmony_ci#include <asm/time.h> 288c2ecf20Sopenharmony_ci#include <asm/mc146818-time.h> 298c2ecf20Sopenharmony_ci#include <asm/msc01_ic.h> 308c2ecf20Sopenharmony_ci#include <asm/mips-cps.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <asm/mips-boards/generic.h> 338c2ecf20Sopenharmony_ci#include <asm/mips-boards/maltaint.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int mips_cpu_timer_irq; 368c2ecf20Sopenharmony_cistatic int mips_cpu_perf_irq; 378c2ecf20Sopenharmony_ciextern int cp0_perfcount_irq; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic unsigned int gic_frequency; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void mips_timer_dispatch(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci do_IRQ(mips_cpu_timer_irq); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void mips_perf_dispatch(void) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci do_IRQ(mips_cpu_perf_irq); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic unsigned int freqround(unsigned int freq, unsigned int amount) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci freq += amount; 548c2ecf20Sopenharmony_ci freq -= freq % (amount*2); 558c2ecf20Sopenharmony_ci return freq; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Estimate CPU and GIC frequencies. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic void __init estimate_frequencies(void) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci unsigned long flags; 648c2ecf20Sopenharmony_ci unsigned int count, start; 658c2ecf20Sopenharmony_ci unsigned char secs1, secs2, ctrl; 668c2ecf20Sopenharmony_ci int secs; 678c2ecf20Sopenharmony_ci u64 giccount = 0, gicstart = 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#if defined(CONFIG_KVM_GUEST) && CONFIG_KVM_GUEST_TIMER_FREQ 708c2ecf20Sopenharmony_ci mips_hpt_frequency = CONFIG_KVM_GUEST_TIMER_FREQ * 1000000; 718c2ecf20Sopenharmony_ci return; 728c2ecf20Sopenharmony_ci#endif 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci local_irq_save(flags); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (mips_gic_present()) 778c2ecf20Sopenharmony_ci clear_gic_config(GIC_CONFIG_COUNTSTOP); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Read counters exactly on rising edge of update flag. 818c2ecf20Sopenharmony_ci * This helps get an accurate reading under virtualisation. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci while (CMOS_READ(RTC_REG_A) & RTC_UIP); 848c2ecf20Sopenharmony_ci while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); 858c2ecf20Sopenharmony_ci start = read_c0_count(); 868c2ecf20Sopenharmony_ci if (mips_gic_present()) 878c2ecf20Sopenharmony_ci gicstart = read_gic_counter(); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Wait for falling edge before reading RTC. */ 908c2ecf20Sopenharmony_ci while (CMOS_READ(RTC_REG_A) & RTC_UIP); 918c2ecf20Sopenharmony_ci secs1 = CMOS_READ(RTC_SECONDS); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Read counters again exactly on rising edge of update flag. */ 948c2ecf20Sopenharmony_ci while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); 958c2ecf20Sopenharmony_ci count = read_c0_count(); 968c2ecf20Sopenharmony_ci if (mips_gic_present()) 978c2ecf20Sopenharmony_ci giccount = read_gic_counter(); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Wait for falling edge before reading RTC again. */ 1008c2ecf20Sopenharmony_ci while (CMOS_READ(RTC_REG_A) & RTC_UIP); 1018c2ecf20Sopenharmony_ci secs2 = CMOS_READ(RTC_SECONDS); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ctrl = CMOS_READ(RTC_CONTROL); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci local_irq_restore(flags); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 1088c2ecf20Sopenharmony_ci secs1 = bcd2bin(secs1); 1098c2ecf20Sopenharmony_ci secs2 = bcd2bin(secs2); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci secs = secs2 - secs1; 1128c2ecf20Sopenharmony_ci if (secs < 1) 1138c2ecf20Sopenharmony_ci secs += 60; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci count -= start; 1168c2ecf20Sopenharmony_ci count /= secs; 1178c2ecf20Sopenharmony_ci mips_hpt_frequency = count; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (mips_gic_present()) { 1208c2ecf20Sopenharmony_ci giccount = div_u64(giccount - gicstart, secs); 1218c2ecf20Sopenharmony_ci gic_frequency = giccount; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_civoid read_persistent_clock64(struct timespec64 *ts) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci ts->tv_sec = mc146818_get_cmos_time(); 1288c2ecf20Sopenharmony_ci ts->tv_nsec = 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint get_c0_fdc_int(void) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * Some cores claim the FDC is routable through the GIC, but it doesn't 1358c2ecf20Sopenharmony_ci * actually seem to be connected for those Malta bitstreams. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 1388c2ecf20Sopenharmony_ci case CPU_INTERAPTIV: 1398c2ecf20Sopenharmony_ci case CPU_PROAPTIV: 1408c2ecf20Sopenharmony_ci return -1; 1418c2ecf20Sopenharmony_ci }; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (cpu_has_veic) 1448c2ecf20Sopenharmony_ci return -1; 1458c2ecf20Sopenharmony_ci else if (mips_gic_present()) 1468c2ecf20Sopenharmony_ci return gic_get_c0_fdc_int(); 1478c2ecf20Sopenharmony_ci else if (cp0_fdc_irq >= 0) 1488c2ecf20Sopenharmony_ci return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci return -1; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciint get_c0_perfcount_int(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci if (cpu_has_veic) { 1568c2ecf20Sopenharmony_ci set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch); 1578c2ecf20Sopenharmony_ci mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR; 1588c2ecf20Sopenharmony_ci } else if (mips_gic_present()) { 1598c2ecf20Sopenharmony_ci mips_cpu_perf_irq = gic_get_c0_perfcount_int(); 1608c2ecf20Sopenharmony_ci } else if (cp0_perfcount_irq >= 0) { 1618c2ecf20Sopenharmony_ci mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci mips_cpu_perf_irq = -1; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return mips_cpu_perf_irq; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(get_c0_perfcount_int); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciunsigned int get_c0_compare_int(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci if (cpu_has_veic) { 1738c2ecf20Sopenharmony_ci set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch); 1748c2ecf20Sopenharmony_ci mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; 1758c2ecf20Sopenharmony_ci } else if (mips_gic_present()) { 1768c2ecf20Sopenharmony_ci mips_cpu_timer_irq = gic_get_c0_compare_int(); 1778c2ecf20Sopenharmony_ci } else { 1788c2ecf20Sopenharmony_ci mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return mips_cpu_timer_irq; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void __init init_rtc(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci unsigned char freq, ctrl; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Set 32KHz time base if not already set */ 1898c2ecf20Sopenharmony_ci freq = CMOS_READ(RTC_FREQ_SELECT); 1908c2ecf20Sopenharmony_ci if ((freq & RTC_DIV_CTL) != RTC_REF_CLCK_32KHZ) 1918c2ecf20Sopenharmony_ci CMOS_WRITE(RTC_REF_CLCK_32KHZ, RTC_FREQ_SELECT); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Ensure SET bit is clear so RTC can run */ 1948c2ecf20Sopenharmony_ci ctrl = CMOS_READ(RTC_CONTROL); 1958c2ecf20Sopenharmony_ci if (ctrl & RTC_SET) 1968c2ecf20Sopenharmony_ci CMOS_WRITE(ctrl & ~RTC_SET, RTC_CONTROL); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC 2008c2ecf20Sopenharmony_cistatic u32 gic_frequency_dt; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct property gic_frequency_prop = { 2038c2ecf20Sopenharmony_ci .name = "clock-frequency", 2048c2ecf20Sopenharmony_ci .length = sizeof(u32), 2058c2ecf20Sopenharmony_ci .value = &gic_frequency_dt, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void update_gic_frequency_dt(void) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct device_node *node; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci gic_frequency_dt = cpu_to_be32(gic_frequency); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "mti,gic-timer"); 2158c2ecf20Sopenharmony_ci if (!node) { 2168c2ecf20Sopenharmony_ci pr_err("mti,gic-timer device node not found\n"); 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (of_update_property(node, &gic_frequency_prop) < 0) 2218c2ecf20Sopenharmony_ci pr_err("error updating gic frequency property\n"); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#endif 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid __init plat_time_init(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK); 2298c2ecf20Sopenharmony_ci unsigned int freq; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci init_rtc(); 2328c2ecf20Sopenharmony_ci estimate_frequencies(); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci freq = mips_hpt_frequency; 2358c2ecf20Sopenharmony_ci if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && 2368c2ecf20Sopenharmony_ci (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) 2378c2ecf20Sopenharmony_ci freq *= 2; 2388c2ecf20Sopenharmony_ci freq = freqround(freq, 5000); 2398c2ecf20Sopenharmony_ci printk("CPU frequency %d.%02d MHz\n", freq/1000000, 2408c2ecf20Sopenharmony_ci (freq%1000000)*100/1000000); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#ifdef CONFIG_I8253 2438c2ecf20Sopenharmony_ci /* Only Malta has a PIT. */ 2448c2ecf20Sopenharmony_ci setup_pit_timer(); 2458c2ecf20Sopenharmony_ci#endif 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (mips_gic_present()) { 2488c2ecf20Sopenharmony_ci freq = freqround(gic_frequency, 5000); 2498c2ecf20Sopenharmony_ci printk("GIC frequency %d.%02d MHz\n", freq/1000000, 2508c2ecf20Sopenharmony_ci (freq%1000000)*100/1000000); 2518c2ecf20Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC 2528c2ecf20Sopenharmony_ci update_gic_frequency_dt(); 2538c2ecf20Sopenharmony_ci timer_probe(); 2548c2ecf20Sopenharmony_ci#endif 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci} 257