Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file is subject to the terms and conditions of the GNU General Public
0003  * License.  See the file "COPYING" in the main directory of this archive
0004  * for more details.
0005  *
0006  * Copyright (C) 2007 by Ralf Baechle
0007  */
0008 #include <linux/clocksource.h>
0009 #include <linux/cpufreq.h>
0010 #include <linux/init.h>
0011 #include <linux/sched_clock.h>
0012 
0013 #include <asm/time.h>
0014 
0015 static u64 c0_hpt_read(struct clocksource *cs)
0016 {
0017     return read_c0_count();
0018 }
0019 
0020 static struct clocksource clocksource_mips = {
0021     .name       = "MIPS",
0022     .read       = c0_hpt_read,
0023     .mask       = CLOCKSOURCE_MASK(32),
0024     .flags      = CLOCK_SOURCE_IS_CONTINUOUS,
0025 };
0026 
0027 static u64 __maybe_unused notrace r4k_read_sched_clock(void)
0028 {
0029     return read_c0_count();
0030 }
0031 
0032 static inline unsigned int rdhwr_count(void)
0033 {
0034     unsigned int count;
0035 
0036     __asm__ __volatile__(
0037     "   .set push\n"
0038     "   .set mips32r2\n"
0039     "   rdhwr   %0, $2\n"
0040     "   .set pop\n"
0041     : "=r" (count));
0042 
0043     return count;
0044 }
0045 
0046 static bool rdhwr_count_usable(void)
0047 {
0048     unsigned int prev, curr, i;
0049 
0050     /*
0051      * Older QEMUs have a broken implementation of RDHWR for the CP0 count
0052      * which always returns a constant value. Try to identify this and don't
0053      * use it in the VDSO if it is broken. This workaround can be removed
0054      * once the fix has been in QEMU stable for a reasonable amount of time.
0055      */
0056     for (i = 0, prev = rdhwr_count(); i < 100; i++) {
0057         curr = rdhwr_count();
0058 
0059         if (curr != prev)
0060             return true;
0061 
0062         prev = curr;
0063     }
0064 
0065     pr_warn("Not using R4K clocksource in VDSO due to broken RDHWR\n");
0066     return false;
0067 }
0068 
0069 #ifdef CONFIG_CPU_FREQ
0070 
0071 static bool __read_mostly r4k_clock_unstable;
0072 
0073 static void r4k_clocksource_unstable(char *reason)
0074 {
0075     if (r4k_clock_unstable)
0076         return;
0077 
0078     r4k_clock_unstable = true;
0079 
0080     pr_info("R4K timer is unstable due to %s\n", reason);
0081 
0082     clocksource_mark_unstable(&clocksource_mips);
0083 }
0084 
0085 static int r4k_cpufreq_callback(struct notifier_block *nb,
0086                 unsigned long val, void *data)
0087 {
0088     if (val == CPUFREQ_POSTCHANGE)
0089         r4k_clocksource_unstable("CPU frequency change");
0090 
0091     return 0;
0092 }
0093 
0094 static struct notifier_block r4k_cpufreq_notifier = {
0095     .notifier_call  = r4k_cpufreq_callback,
0096 };
0097 
0098 static int __init r4k_register_cpufreq_notifier(void)
0099 {
0100     return cpufreq_register_notifier(&r4k_cpufreq_notifier,
0101                      CPUFREQ_TRANSITION_NOTIFIER);
0102 
0103 }
0104 core_initcall(r4k_register_cpufreq_notifier);
0105 
0106 #endif /* !CONFIG_CPU_FREQ */
0107 
0108 int __init init_r4k_clocksource(void)
0109 {
0110     if (!cpu_has_counter || !mips_hpt_frequency)
0111         return -ENXIO;
0112 
0113     /* Calculate a somewhat reasonable rating value */
0114     clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
0115 
0116     /*
0117      * R2 onwards makes the count accessible to user mode so it can be used
0118      * by the VDSO (HWREna is configured by configure_hwrena()).
0119      */
0120     if (cpu_has_mips_r2_r6 && rdhwr_count_usable())
0121         clocksource_mips.vdso_clock_mode = VDSO_CLOCKMODE_R4K;
0122 
0123     clocksource_register_hz(&clocksource_mips, mips_hpt_frequency);
0124 
0125 #ifndef CONFIG_CPU_FREQ
0126     sched_clock_register(r4k_read_sched_clock, 32, mips_hpt_frequency);
0127 #endif
0128 
0129     return 0;
0130 }