162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Support code for virtual Ranchu board for MIPS.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Miodrag Dinic <miodrag.dinic@mips.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/of_address.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/machine.h>
1262306a36Sopenharmony_ci#include <asm/mipsregs.h>
1362306a36Sopenharmony_ci#include <asm/time.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define GOLDFISH_TIMER_LOW		0x00
1662306a36Sopenharmony_ci#define GOLDFISH_TIMER_HIGH		0x04
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic __init u64 read_rtc_time(void __iomem *base)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	u32 time_low;
2162306a36Sopenharmony_ci	u32 time_high;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	/*
2462306a36Sopenharmony_ci	 * Reading the low address latches the high value
2562306a36Sopenharmony_ci	 * as well so there is no fear that we may read
2662306a36Sopenharmony_ci	 * inaccurate high value.
2762306a36Sopenharmony_ci	 */
2862306a36Sopenharmony_ci	time_low = readl(base + GOLDFISH_TIMER_LOW);
2962306a36Sopenharmony_ci	time_high = readl(base + GOLDFISH_TIMER_HIGH);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return ((u64)time_high << 32) | time_low;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic __init unsigned int ranchu_measure_hpt_freq(void)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	u64 rtc_start, rtc_current, rtc_delta;
3762306a36Sopenharmony_ci	unsigned int start, count;
3862306a36Sopenharmony_ci	struct device_node *np;
3962306a36Sopenharmony_ci	void __iomem *rtc_base;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "google,goldfish-rtc");
4262306a36Sopenharmony_ci	if (!np)
4362306a36Sopenharmony_ci		panic("%s(): Failed to find 'google,goldfish-rtc' dt node!",
4462306a36Sopenharmony_ci		      __func__);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	rtc_base = of_iomap(np, 0);
4762306a36Sopenharmony_ci	of_node_put(np);
4862306a36Sopenharmony_ci	if (!rtc_base)
4962306a36Sopenharmony_ci		panic("%s(): Failed to ioremap Goldfish RTC base!", __func__);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/*
5262306a36Sopenharmony_ci	 * Poll the nanosecond resolution RTC for one
5362306a36Sopenharmony_ci	 * second to calibrate the CPU frequency.
5462306a36Sopenharmony_ci	 */
5562306a36Sopenharmony_ci	rtc_start = read_rtc_time(rtc_base);
5662306a36Sopenharmony_ci	start = read_c0_count();
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	do {
5962306a36Sopenharmony_ci		rtc_current = read_rtc_time(rtc_base);
6062306a36Sopenharmony_ci		rtc_delta = rtc_current - rtc_start;
6162306a36Sopenharmony_ci	} while (rtc_delta < NSEC_PER_SEC);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	count = read_c0_count() - start;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * Make sure the frequency will be a round number.
6762306a36Sopenharmony_ci	 * Without this correction, the returned value may vary
6862306a36Sopenharmony_ci	 * between subsequent emulation executions.
6962306a36Sopenharmony_ci	 *
7062306a36Sopenharmony_ci	 * TODO: Set this value using device tree.
7162306a36Sopenharmony_ci	 */
7262306a36Sopenharmony_ci	count += 5000;
7362306a36Sopenharmony_ci	count -= count % 10000;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	iounmap(rtc_base);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return count;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic const struct of_device_id ranchu_of_match[] __initconst = {
8162306a36Sopenharmony_ci	{
8262306a36Sopenharmony_ci		.compatible = "mti,ranchu",
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{}
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciMIPS_MACHINE(ranchu) = {
8862306a36Sopenharmony_ci	.matches = ranchu_of_match,
8962306a36Sopenharmony_ci	.measure_hpt_freq = ranchu_measure_hpt_freq,
9062306a36Sopenharmony_ci};
91