xref: /openbmc/linux/arch/mips/kernel/csrc-r4k.c (revision a7f4df4e21dd8a8dab96e88acd2c9c5017b83fc6)
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 
148e19608eSMagnus Damm static cycle_t 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 
26e9cef549SDeng-Cheng Zhu static u64 notrace r4k_read_sched_clock(void)
27e9cef549SDeng-Cheng Zhu {
28e9cef549SDeng-Cheng Zhu 	return read_c0_count();
29e9cef549SDeng-Cheng Zhu }
30e9cef549SDeng-Cheng Zhu 
31*a7f4df4eSAlex Smith static inline unsigned int rdhwr_count(void)
32*a7f4df4eSAlex Smith {
33*a7f4df4eSAlex Smith 	unsigned int count;
34*a7f4df4eSAlex Smith 
35*a7f4df4eSAlex Smith 	__asm__ __volatile__(
36*a7f4df4eSAlex Smith 	"	.set push\n"
37*a7f4df4eSAlex Smith 	"	.set mips32r2\n"
38*a7f4df4eSAlex Smith 	"	rdhwr	%0, $2\n"
39*a7f4df4eSAlex Smith 	"	.set pop\n"
40*a7f4df4eSAlex Smith 	: "=r" (count));
41*a7f4df4eSAlex Smith 
42*a7f4df4eSAlex Smith 	return count;
43*a7f4df4eSAlex Smith }
44*a7f4df4eSAlex Smith 
45*a7f4df4eSAlex Smith static bool rdhwr_count_usable(void)
46*a7f4df4eSAlex Smith {
47*a7f4df4eSAlex Smith 	unsigned int prev, curr, i;
48*a7f4df4eSAlex Smith 
49*a7f4df4eSAlex Smith 	/*
50*a7f4df4eSAlex Smith 	 * Older QEMUs have a broken implementation of RDHWR for the CP0 count
51*a7f4df4eSAlex Smith 	 * which always returns a constant value. Try to identify this and don't
52*a7f4df4eSAlex Smith 	 * use it in the VDSO if it is broken. This workaround can be removed
53*a7f4df4eSAlex Smith 	 * once the fix has been in QEMU stable for a reasonable amount of time.
54*a7f4df4eSAlex Smith 	 */
55*a7f4df4eSAlex Smith 	for (i = 0, prev = rdhwr_count(); i < 100; i++) {
56*a7f4df4eSAlex Smith 		curr = rdhwr_count();
57*a7f4df4eSAlex Smith 
58*a7f4df4eSAlex Smith 		if (curr != prev)
59*a7f4df4eSAlex Smith 			return true;
60*a7f4df4eSAlex Smith 
61*a7f4df4eSAlex Smith 		prev = curr;
62*a7f4df4eSAlex Smith 	}
63*a7f4df4eSAlex Smith 
64*a7f4df4eSAlex Smith 	pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n");
65*a7f4df4eSAlex Smith 	return false;
66*a7f4df4eSAlex Smith }
67*a7f4df4eSAlex 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 
76*a7f4df4eSAlex Smith 	/*
77*a7f4df4eSAlex Smith 	 * R2 onwards makes the count accessible to user mode so it can be used
78*a7f4df4eSAlex Smith 	 * by the VDSO (HWREna is configured by configure_hwrena()).
79*a7f4df4eSAlex Smith 	 */
80*a7f4df4eSAlex Smith 	if (cpu_has_mips_r2_r6 && rdhwr_count_usable())
81*a7f4df4eSAlex Smith 		clocksource_mips.archdata.vdso_clock_mode = VDSO_CLOCK_R4K;
82*a7f4df4eSAlex Smith 
8375c4fd8cSJohn Stultz 	clocksource_register_hz(&clocksource_mips, mips_hpt_frequency);
8469e634f1SRalf Baechle 
85e9cef549SDeng-Cheng Zhu 	sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency);
86e9cef549SDeng-Cheng Zhu 
8769e634f1SRalf Baechle 	return 0;
88940f6b48SRalf Baechle }
89