1 /* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2007 by Ralf Baechle 7 */ 8 #include <linux/clocksource.h> 9 #include <linux/init.h> 10 #include <linux/sched_clock.h> 11 12 #include <asm/time.h> 13 14 static cycle_t c0_hpt_read(struct clocksource *cs) 15 { 16 return read_c0_count(); 17 } 18 19 static struct clocksource clocksource_mips = { 20 .name = "MIPS", 21 .read = c0_hpt_read, 22 .mask = CLOCKSOURCE_MASK(32), 23 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 24 }; 25 26 static u64 __maybe_unused notrace r4k_read_sched_clock(void) 27 { 28 return read_c0_count(); 29 } 30 31 static inline unsigned int rdhwr_count(void) 32 { 33 unsigned int count; 34 35 __asm__ __volatile__( 36 " .set push\n" 37 " .set mips32r2\n" 38 " rdhwr %0, $2\n" 39 " .set pop\n" 40 : "=r" (count)); 41 42 return count; 43 } 44 45 static bool rdhwr_count_usable(void) 46 { 47 unsigned int prev, curr, i; 48 49 /* 50 * Older QEMUs have a broken implementation of RDHWR for the CP0 count 51 * which always returns a constant value. Try to identify this and don't 52 * use it in the VDSO if it is broken. This workaround can be removed 53 * once the fix has been in QEMU stable for a reasonable amount of time. 54 */ 55 for (i = 0, prev = rdhwr_count(); i < 100; i++) { 56 curr = rdhwr_count(); 57 58 if (curr != prev) 59 return true; 60 61 prev = curr; 62 } 63 64 pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n"); 65 return false; 66 } 67 68 int __init init_r4k_clocksource(void) 69 { 70 if (!cpu_has_counter || !mips_hpt_frequency) 71 return -ENXIO; 72 73 /* Calculate a somewhat reasonable rating value */ 74 clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000; 75 76 /* 77 * R2 onwards makes the count accessible to user mode so it can be used 78 * by the VDSO (HWREna is configured by configure_hwrena()). 79 */ 80 if (cpu_has_mips_r2_r6 && rdhwr_count_usable()) 81 clocksource_mips.archdata.vdso_clock_mode = VDSO_CLOCK_R4K; 82 83 clocksource_register_hz(&clocksource_mips, mips_hpt_frequency); 84 85 #ifndef CONFIG_CPU_FREQ 86 sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency); 87 #endif 88 89 return 0; 90 } 91