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