Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2007 by Ralf Baechle */ #include <linux/clocksource.h> #include <linux/cpufreq.h> #include <linux/init.h> #include <linux/sched_clock.h> #include <asm/time.h> static u64 c0_hpt_read(struct clocksource *cs) { return read_c0_count(); } static struct clocksource clocksource_mips = { .name = "MIPS", .read = c0_hpt_read, .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static u64 __maybe_unused notrace r4k_read_sched_clock(void) { return read_c0_count(); } static inline unsigned int rdhwr_count(void) { unsigned int count; __asm__ __volatile__( " .set push\n" " .set mips32r2\n" " rdhwr %0, $2\n" " .set pop\n" : "=r" (count)); return count; } static bool rdhwr_count_usable(void) { unsigned int prev, curr, i; /* * Older QEMUs have a broken implementation of RDHWR for the CP0 count * which always returns a constant value. Try to identify this and don't * use it in the VDSO if it is broken. This workaround can be removed * once the fix has been in QEMU stable for a reasonable amount of time. */ for (i = 0, prev = rdhwr_count(); i < 100; i++) { curr = rdhwr_count(); if (curr != prev) return true; prev = curr; } pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n"); return false; } #ifdef CONFIG_CPU_FREQ static bool __read_mostly r4k_clock_unstable; static void r4k_clocksource_unstable(char *reason) { if (r4k_clock_unstable) return; r4k_clock_unstable = true; pr_info("R4K timer is unstable due to %s\n", reason); clocksource_mark_unstable(&clocksource_mips); } static int r4k_cpufreq_callback(struct notifier_block *nb, unsigned long val, void *data) { if (val == CPUFREQ_POSTCHANGE) r4k_clocksource_unstable("CPU frequency change"); return 0; } static struct notifier_block r4k_cpufreq_notifier = { .notifier_call = r4k_cpufreq_callback, }; static int __init r4k_register_cpufreq_notifier(void) { return cpufreq_register_notifier(&r4k_cpufreq_notifier, CPUFREQ_TRANSITION_NOTIFIER); } core_initcall(r4k_register_cpufreq_notifier); #endif /* !CONFIG_CPU_FREQ */ int __init init_r4k_clocksource(void) { if (!cpu_has_counter || !mips_hpt_frequency) return -ENXIO; /* Calculate a somewhat reasonable rating value */ clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000; /* * R2 onwards makes the count accessible to user mode so it can be used * by the VDSO (HWREna is configured by configure_hwrena()). */ if (cpu_has_mips_r2_r6 && rdhwr_count_usable()) clocksource_mips.vdso_clock_mode = VDSO_CLOCKMODE_R4K; clocksource_register_hz(&clocksource_mips, mips_hpt_frequency); #ifndef CONFIG_CPU_FREQ sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency); #endif return 0; } |