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> 9*38586428SSerge Semin #include <linux/cpufreq.h> 10e6a1bb72SRalf Baechle #include <linux/init.h> 11e9cef549SDeng-Cheng Zhu #include <linux/sched_clock.h> 12e6a1bb72SRalf Baechle 13e6a1bb72SRalf Baechle #include <asm/time.h> 14940f6b48SRalf Baechle 15a5a1d1c2SThomas Gleixner static u64 c0_hpt_read(struct clocksource *cs) 16940f6b48SRalf Baechle { 17940f6b48SRalf Baechle return read_c0_count(); 18940f6b48SRalf Baechle } 19940f6b48SRalf Baechle 20940f6b48SRalf Baechle static struct clocksource clocksource_mips = { 21940f6b48SRalf Baechle .name = "MIPS", 22940f6b48SRalf Baechle .read = c0_hpt_read, 23940f6b48SRalf Baechle .mask = CLOCKSOURCE_MASK(32), 24940f6b48SRalf Baechle .flags = CLOCK_SOURCE_IS_CONTINUOUS, 25940f6b48SRalf Baechle }; 26940f6b48SRalf Baechle 2707d69579SHuacai Chen static u64 __maybe_unused notrace r4k_read_sched_clock(void) 28e9cef549SDeng-Cheng Zhu { 29e9cef549SDeng-Cheng Zhu return read_c0_count(); 30e9cef549SDeng-Cheng Zhu } 31e9cef549SDeng-Cheng Zhu 32a7f4df4eSAlex Smith static inline unsigned int rdhwr_count(void) 33a7f4df4eSAlex Smith { 34a7f4df4eSAlex Smith unsigned int count; 35a7f4df4eSAlex Smith 36a7f4df4eSAlex Smith __asm__ __volatile__( 37a7f4df4eSAlex Smith " .set push\n" 38a7f4df4eSAlex Smith " .set mips32r2\n" 39a7f4df4eSAlex Smith " rdhwr %0, $2\n" 40a7f4df4eSAlex Smith " .set pop\n" 41a7f4df4eSAlex Smith : "=r" (count)); 42a7f4df4eSAlex Smith 43a7f4df4eSAlex Smith return count; 44a7f4df4eSAlex Smith } 45a7f4df4eSAlex Smith 46a7f4df4eSAlex Smith static bool rdhwr_count_usable(void) 47a7f4df4eSAlex Smith { 48a7f4df4eSAlex Smith unsigned int prev, curr, i; 49a7f4df4eSAlex Smith 50a7f4df4eSAlex Smith /* 51a7f4df4eSAlex Smith * Older QEMUs have a broken implementation of RDHWR for the CP0 count 52a7f4df4eSAlex Smith * which always returns a constant value. Try to identify this and don't 53a7f4df4eSAlex Smith * use it in the VDSO if it is broken. This workaround can be removed 54a7f4df4eSAlex Smith * once the fix has been in QEMU stable for a reasonable amount of time. 55a7f4df4eSAlex Smith */ 56a7f4df4eSAlex Smith for (i = 0, prev = rdhwr_count(); i < 100; i++) { 57a7f4df4eSAlex Smith curr = rdhwr_count(); 58a7f4df4eSAlex Smith 59a7f4df4eSAlex Smith if (curr != prev) 60a7f4df4eSAlex Smith return true; 61a7f4df4eSAlex Smith 62a7f4df4eSAlex Smith prev = curr; 63a7f4df4eSAlex Smith } 64a7f4df4eSAlex Smith 65a7f4df4eSAlex Smith pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n"); 66a7f4df4eSAlex Smith return false; 67a7f4df4eSAlex Smith } 68a7f4df4eSAlex Smith 69*38586428SSerge Semin #ifdef CONFIG_CPU_FREQ 70*38586428SSerge Semin 71*38586428SSerge Semin static bool __read_mostly r4k_clock_unstable; 72*38586428SSerge Semin 73*38586428SSerge Semin static void r4k_clocksource_unstable(char *reason) 74*38586428SSerge Semin { 75*38586428SSerge Semin if (r4k_clock_unstable) 76*38586428SSerge Semin return; 77*38586428SSerge Semin 78*38586428SSerge Semin r4k_clock_unstable = true; 79*38586428SSerge Semin 80*38586428SSerge Semin pr_info("R4K timer is unstable due to %s\n", reason); 81*38586428SSerge Semin 82*38586428SSerge Semin clocksource_mark_unstable(&clocksource_mips); 83*38586428SSerge Semin } 84*38586428SSerge Semin 85*38586428SSerge Semin static int r4k_cpufreq_callback(struct notifier_block *nb, 86*38586428SSerge Semin unsigned long val, void *data) 87*38586428SSerge Semin { 88*38586428SSerge Semin if (val == CPUFREQ_POSTCHANGE) 89*38586428SSerge Semin r4k_clocksource_unstable("CPU frequency change"); 90*38586428SSerge Semin 91*38586428SSerge Semin return 0; 92*38586428SSerge Semin } 93*38586428SSerge Semin 94*38586428SSerge Semin static struct notifier_block r4k_cpufreq_notifier = { 95*38586428SSerge Semin .notifier_call = r4k_cpufreq_callback, 96*38586428SSerge Semin }; 97*38586428SSerge Semin 98*38586428SSerge Semin static int __init r4k_register_cpufreq_notifier(void) 99*38586428SSerge Semin { 100*38586428SSerge Semin return cpufreq_register_notifier(&r4k_cpufreq_notifier, 101*38586428SSerge Semin CPUFREQ_TRANSITION_NOTIFIER); 102*38586428SSerge Semin 103*38586428SSerge Semin } 104*38586428SSerge Semin core_initcall(r4k_register_cpufreq_notifier); 105*38586428SSerge Semin 106*38586428SSerge Semin #endif /* !CONFIG_CPU_FREQ */ 107*38586428SSerge Semin 108779e7d41SManuel Lauss int __init init_r4k_clocksource(void) 109940f6b48SRalf Baechle { 11069e634f1SRalf Baechle if (!cpu_has_counter || !mips_hpt_frequency) 11169e634f1SRalf Baechle return -ENXIO; 11269e634f1SRalf Baechle 113664c4bbbSRalf Baechle /* Calculate a somewhat reasonable rating value */ 114940f6b48SRalf Baechle clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000; 115940f6b48SRalf Baechle 116a7f4df4eSAlex Smith /* 117a7f4df4eSAlex Smith * R2 onwards makes the count accessible to user mode so it can be used 118a7f4df4eSAlex Smith * by the VDSO (HWREna is configured by configure_hwrena()). 119a7f4df4eSAlex Smith */ 120a7f4df4eSAlex Smith if (cpu_has_mips_r2_r6 && rdhwr_count_usable()) 121e1bdb22eSThomas Gleixner clocksource_mips.vdso_clock_mode = VDSO_CLOCKMODE_R4K; 122a7f4df4eSAlex Smith 12375c4fd8cSJohn Stultz clocksource_register_hz(&clocksource_mips, mips_hpt_frequency); 12469e634f1SRalf Baechle 12507d69579SHuacai Chen #ifndef CONFIG_CPU_FREQ 126e9cef549SDeng-Cheng Zhu sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency); 12707d69579SHuacai Chen #endif 128e9cef549SDeng-Cheng Zhu 12969e634f1SRalf Baechle return 0; 130940f6b48SRalf Baechle } 131