162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com
462306a36Sopenharmony_ci * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Setting up the clock on the MIPS boards.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci#include <linux/i8253.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/kernel_stat.h>
1262306a36Sopenharmony_ci#include <linux/libfdt.h>
1362306a36Sopenharmony_ci#include <linux/math64.h>
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/spinlock.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/timex.h>
1862306a36Sopenharmony_ci#include <linux/mc146818rtc.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/cpu.h>
2162306a36Sopenharmony_ci#include <asm/mipsregs.h>
2262306a36Sopenharmony_ci#include <asm/mipsmtregs.h>
2362306a36Sopenharmony_ci#include <asm/hardirq.h>
2462306a36Sopenharmony_ci#include <asm/irq.h>
2562306a36Sopenharmony_ci#include <asm/div64.h>
2662306a36Sopenharmony_ci#include <asm/setup.h>
2762306a36Sopenharmony_ci#include <asm/time.h>
2862306a36Sopenharmony_ci#include <asm/mc146818-time.h>
2962306a36Sopenharmony_ci#include <asm/msc01_ic.h>
3062306a36Sopenharmony_ci#include <asm/mips-cps.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <asm/mips-boards/generic.h>
3362306a36Sopenharmony_ci#include <asm/mips-boards/maltaint.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int mips_cpu_timer_irq;
3662306a36Sopenharmony_cistatic int mips_cpu_perf_irq;
3762306a36Sopenharmony_ciextern int cp0_perfcount_irq;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic unsigned int gic_frequency;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void mips_timer_dispatch(void)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	do_IRQ(mips_cpu_timer_irq);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void mips_perf_dispatch(void)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	do_IRQ(mips_cpu_perf_irq);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic unsigned int freqround(unsigned int freq, unsigned int amount)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	freq += amount;
5462306a36Sopenharmony_ci	freq -= freq % (amount*2);
5562306a36Sopenharmony_ci	return freq;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Estimate CPU and GIC frequencies.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistatic void __init estimate_frequencies(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	unsigned long flags;
6462306a36Sopenharmony_ci	unsigned int count, start;
6562306a36Sopenharmony_ci	unsigned char secs1, secs2, ctrl;
6662306a36Sopenharmony_ci	int secs;
6762306a36Sopenharmony_ci	u64 giccount = 0, gicstart = 0;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	local_irq_save(flags);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (mips_gic_present())
7262306a36Sopenharmony_ci		clear_gic_config(GIC_CONFIG_COUNTSTOP);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * Read counters exactly on rising edge of update flag.
7662306a36Sopenharmony_ci	 * This helps get an accurate reading under virtualisation.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	while (CMOS_READ(RTC_REG_A) & RTC_UIP);
7962306a36Sopenharmony_ci	while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
8062306a36Sopenharmony_ci	start = read_c0_count();
8162306a36Sopenharmony_ci	if (mips_gic_present())
8262306a36Sopenharmony_ci		gicstart = read_gic_counter();
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Wait for falling edge before reading RTC. */
8562306a36Sopenharmony_ci	while (CMOS_READ(RTC_REG_A) & RTC_UIP);
8662306a36Sopenharmony_ci	secs1 = CMOS_READ(RTC_SECONDS);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Read counters again exactly on rising edge of update flag. */
8962306a36Sopenharmony_ci	while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
9062306a36Sopenharmony_ci	count = read_c0_count();
9162306a36Sopenharmony_ci	if (mips_gic_present())
9262306a36Sopenharmony_ci		giccount = read_gic_counter();
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Wait for falling edge before reading RTC again. */
9562306a36Sopenharmony_ci	while (CMOS_READ(RTC_REG_A) & RTC_UIP);
9662306a36Sopenharmony_ci	secs2 = CMOS_READ(RTC_SECONDS);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ctrl = CMOS_READ(RTC_CONTROL);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	local_irq_restore(flags);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
10362306a36Sopenharmony_ci		secs1 = bcd2bin(secs1);
10462306a36Sopenharmony_ci		secs2 = bcd2bin(secs2);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	secs = secs2 - secs1;
10762306a36Sopenharmony_ci	if (secs < 1)
10862306a36Sopenharmony_ci		secs += 60;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	count -= start;
11162306a36Sopenharmony_ci	count /= secs;
11262306a36Sopenharmony_ci	mips_hpt_frequency = count;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (mips_gic_present()) {
11562306a36Sopenharmony_ci		giccount = div_u64(giccount - gicstart, secs);
11662306a36Sopenharmony_ci		gic_frequency = giccount;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid read_persistent_clock64(struct timespec64 *ts)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	ts->tv_sec = mc146818_get_cmos_time();
12362306a36Sopenharmony_ci	ts->tv_nsec = 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint get_c0_fdc_int(void)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * Some cores claim the FDC is routable through the GIC, but it doesn't
13062306a36Sopenharmony_ci	 * actually seem to be connected for those Malta bitstreams.
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	switch (current_cpu_type()) {
13362306a36Sopenharmony_ci	case CPU_INTERAPTIV:
13462306a36Sopenharmony_ci	case CPU_PROAPTIV:
13562306a36Sopenharmony_ci		return -1;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (cpu_has_veic)
13962306a36Sopenharmony_ci		return -1;
14062306a36Sopenharmony_ci	else if (mips_gic_present())
14162306a36Sopenharmony_ci		return gic_get_c0_fdc_int();
14262306a36Sopenharmony_ci	else if (cp0_fdc_irq >= 0)
14362306a36Sopenharmony_ci		return MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
14462306a36Sopenharmony_ci	else
14562306a36Sopenharmony_ci		return -1;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciint get_c0_perfcount_int(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (cpu_has_veic) {
15162306a36Sopenharmony_ci		set_vi_handler(MSC01E_INT_PERFCTR, mips_perf_dispatch);
15262306a36Sopenharmony_ci		mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
15362306a36Sopenharmony_ci	} else if (mips_gic_present()) {
15462306a36Sopenharmony_ci		mips_cpu_perf_irq = gic_get_c0_perfcount_int();
15562306a36Sopenharmony_ci	} else if (cp0_perfcount_irq >= 0) {
15662306a36Sopenharmony_ci		mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
15762306a36Sopenharmony_ci	} else {
15862306a36Sopenharmony_ci		mips_cpu_perf_irq = -1;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return mips_cpu_perf_irq;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_c0_perfcount_int);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciunsigned int get_c0_compare_int(void)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	if (cpu_has_veic) {
16862306a36Sopenharmony_ci		set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch);
16962306a36Sopenharmony_ci		mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
17062306a36Sopenharmony_ci	} else if (mips_gic_present()) {
17162306a36Sopenharmony_ci		mips_cpu_timer_irq = gic_get_c0_compare_int();
17262306a36Sopenharmony_ci	} else {
17362306a36Sopenharmony_ci		mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return mips_cpu_timer_irq;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void __init init_rtc(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	unsigned char freq, ctrl;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Set 32KHz time base if not already set */
18462306a36Sopenharmony_ci	freq = CMOS_READ(RTC_FREQ_SELECT);
18562306a36Sopenharmony_ci	if ((freq & RTC_DIV_CTL) != RTC_REF_CLCK_32KHZ)
18662306a36Sopenharmony_ci		CMOS_WRITE(RTC_REF_CLCK_32KHZ, RTC_FREQ_SELECT);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Ensure SET bit is clear so RTC can run */
18962306a36Sopenharmony_ci	ctrl = CMOS_READ(RTC_CONTROL);
19062306a36Sopenharmony_ci	if (ctrl & RTC_SET)
19162306a36Sopenharmony_ci		CMOS_WRITE(ctrl & ~RTC_SET, RTC_CONTROL);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC
19562306a36Sopenharmony_cistatic u32 gic_frequency_dt;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct property gic_frequency_prop = {
19862306a36Sopenharmony_ci	.name = "clock-frequency",
19962306a36Sopenharmony_ci	.length = sizeof(u32),
20062306a36Sopenharmony_ci	.value = &gic_frequency_dt,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void update_gic_frequency_dt(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct device_node *node;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	gic_frequency_dt = cpu_to_be32(gic_frequency);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "mti,gic-timer");
21062306a36Sopenharmony_ci	if (!node) {
21162306a36Sopenharmony_ci		pr_err("mti,gic-timer device node not found\n");
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (of_update_property(node, &gic_frequency_prop) < 0)
21662306a36Sopenharmony_ci		pr_err("error updating gic frequency property\n");
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	of_node_put(node);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci#endif
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_civoid __init plat_time_init(void)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK);
22662306a36Sopenharmony_ci	unsigned int freq;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	init_rtc();
22962306a36Sopenharmony_ci	estimate_frequencies();
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	freq = mips_hpt_frequency;
23262306a36Sopenharmony_ci	if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
23362306a36Sopenharmony_ci	    (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
23462306a36Sopenharmony_ci		freq *= 2;
23562306a36Sopenharmony_ci	freq = freqround(freq, 5000);
23662306a36Sopenharmony_ci	printk("CPU frequency %d.%02d MHz\n", freq/1000000,
23762306a36Sopenharmony_ci	       (freq%1000000)*100/1000000);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci#ifdef CONFIG_I8253
24062306a36Sopenharmony_ci	/* Only Malta has a PIT. */
24162306a36Sopenharmony_ci	setup_pit_timer();
24262306a36Sopenharmony_ci#endif
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (mips_gic_present()) {
24562306a36Sopenharmony_ci		freq = freqround(gic_frequency, 5000);
24662306a36Sopenharmony_ci		printk("GIC frequency %d.%02d MHz\n", freq/1000000,
24762306a36Sopenharmony_ci		       (freq%1000000)*100/1000000);
24862306a36Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC
24962306a36Sopenharmony_ci		update_gic_frequency_dt();
25062306a36Sopenharmony_ci		timer_probe();
25162306a36Sopenharmony_ci#endif
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci}
254