xref: /openbmc/linux/arch/mips/kernel/csrc-r4k.c (revision e1bdb22ebe5363ed75ddedf836ca9f19e1195337)
1940f6b48SRalf Baechle /*
2940f6b48SRalf Baechle  * This file is subject to the terms and conditions of the GNU General Public
3940f6b48SRalf Baechle  * License.  See the file "COPYING" in the main directory of this archive
4940f6b48SRalf Baechle  * for more details.
5940f6b48SRalf Baechle  *
6940f6b48SRalf Baechle  * Copyright (C) 2007 by Ralf Baechle
7940f6b48SRalf Baechle  */
8e6a1bb72SRalf Baechle #include <linux/clocksource.h>
9e6a1bb72SRalf Baechle #include <linux/init.h>
10e9cef549SDeng-Cheng Zhu #include <linux/sched_clock.h>
11e6a1bb72SRalf Baechle 
12e6a1bb72SRalf Baechle #include <asm/time.h>
13940f6b48SRalf Baechle 
14a5a1d1c2SThomas Gleixner static u64 c0_hpt_read(struct clocksource *cs)
15940f6b48SRalf Baechle {
16940f6b48SRalf Baechle 	return read_c0_count();
17940f6b48SRalf Baechle }
18940f6b48SRalf Baechle 
19940f6b48SRalf Baechle static struct clocksource clocksource_mips = {
20940f6b48SRalf Baechle 	.name		= "MIPS",
21940f6b48SRalf Baechle 	.read		= c0_hpt_read,
22940f6b48SRalf Baechle 	.mask		= CLOCKSOURCE_MASK(32),
23940f6b48SRalf Baechle 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
24940f6b48SRalf Baechle };
25940f6b48SRalf Baechle 
2607d69579SHuacai Chen static u64 __maybe_unused notrace r4k_read_sched_clock(void)
27e9cef549SDeng-Cheng Zhu {
28e9cef549SDeng-Cheng Zhu 	return read_c0_count();
29e9cef549SDeng-Cheng Zhu }
30e9cef549SDeng-Cheng Zhu 
31a7f4df4eSAlex Smith static inline unsigned int rdhwr_count(void)
32a7f4df4eSAlex Smith {
33a7f4df4eSAlex Smith 	unsigned int count;
34a7f4df4eSAlex Smith 
35a7f4df4eSAlex Smith 	__asm__ __volatile__(
36a7f4df4eSAlex Smith 	"	.set push\n"
37a7f4df4eSAlex Smith 	"	.set mips32r2\n"
38a7f4df4eSAlex Smith 	"	rdhwr	%0, $2\n"
39a7f4df4eSAlex Smith 	"	.set pop\n"
40a7f4df4eSAlex Smith 	: "=r" (count));
41a7f4df4eSAlex Smith 
42a7f4df4eSAlex Smith 	return count;
43a7f4df4eSAlex Smith }
44a7f4df4eSAlex Smith 
45a7f4df4eSAlex Smith static bool rdhwr_count_usable(void)
46a7f4df4eSAlex Smith {
47a7f4df4eSAlex Smith 	unsigned int prev, curr, i;
48a7f4df4eSAlex Smith 
49a7f4df4eSAlex Smith 	/*
50a7f4df4eSAlex Smith 	 * Older QEMUs have a broken implementation of RDHWR for the CP0 count
51a7f4df4eSAlex Smith 	 * which always returns a constant value. Try to identify this and don't
52a7f4df4eSAlex Smith 	 * use it in the VDSO if it is broken. This workaround can be removed
53a7f4df4eSAlex Smith 	 * once the fix has been in QEMU stable for a reasonable amount of time.
54a7f4df4eSAlex Smith 	 */
55a7f4df4eSAlex Smith 	for (i = 0, prev = rdhwr_count(); i < 100; i++) {
56a7f4df4eSAlex Smith 		curr = rdhwr_count();
57a7f4df4eSAlex Smith 
58a7f4df4eSAlex Smith 		if (curr != prev)
59a7f4df4eSAlex Smith 			return true;
60a7f4df4eSAlex Smith 
61a7f4df4eSAlex Smith 		prev = curr;
62a7f4df4eSAlex Smith 	}
63a7f4df4eSAlex Smith 
64a7f4df4eSAlex Smith 	pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n");
65a7f4df4eSAlex Smith 	return false;
66a7f4df4eSAlex Smith }
67a7f4df4eSAlex Smith 
68779e7d41SManuel Lauss int __init init_r4k_clocksource(void)
69940f6b48SRalf Baechle {
7069e634f1SRalf Baechle 	if (!cpu_has_counter || !mips_hpt_frequency)
7169e634f1SRalf Baechle 		return -ENXIO;
7269e634f1SRalf Baechle 
73664c4bbbSRalf Baechle 	/* Calculate a somewhat reasonable rating value */
74940f6b48SRalf Baechle 	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
75940f6b48SRalf Baechle 
76a7f4df4eSAlex Smith 	/*
77a7f4df4eSAlex Smith 	 * R2 onwards makes the count accessible to user mode so it can be used
78a7f4df4eSAlex Smith 	 * by the VDSO (HWREna is configured by configure_hwrena()).
79a7f4df4eSAlex Smith 	 */
80a7f4df4eSAlex Smith 	if (cpu_has_mips_r2_r6 && rdhwr_count_usable())
81*e1bdb22eSThomas Gleixner 		clocksource_mips.vdso_clock_mode = VDSO_CLOCKMODE_R4K;
82a7f4df4eSAlex Smith 
8375c4fd8cSJohn Stultz 	clocksource_register_hz(&clocksource_mips, mips_hpt_frequency);
8469e634f1SRalf Baechle 
8507d69579SHuacai Chen #ifndef CONFIG_CPU_FREQ
86e9cef549SDeng-Cheng Zhu 	sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency);
8707d69579SHuacai Chen #endif
88e9cef549SDeng-Cheng Zhu 
8969e634f1SRalf Baechle 	return 0;
90940f6b48SRalf Baechle }
91