• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/cnt32_to_63.h>
10 #include <linux/timer.h>
11 #include <linux/init.h>
12 
13 #include <asm/time.h>
14 
15 /*
16  * MIPS' sched_clock implementation.
17  *
18  * because cnt32_to_63() needs to be called at least once per half period to
19  * work properly, and some of the MIPS' frequency is very low, perhaps a kernel
20  * timer is needed to be set up to ensure this requirement is always met.
21  * please refer to  arch/arm/plat-orion/time.c and include/linux/cnt32_to_63.h
22  */
23 static unsigned long __maybe_unused tclk2ns_scale;
24 static unsigned long __maybe_unused tclk2ns_scale_factor;
25 
26 #ifdef CONFIG_HR_SCHED_CLOCK
sched_clock(void)27 unsigned long long notrace sched_clock(void)
28 {
29    unsigned long long v = cnt32_to_63(read_c0_count());
30    return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
31 }
32 #endif
33 
setup_sched_clock(struct clocksource * cs)34 static void __init __maybe_unused setup_sched_clock(struct clocksource *cs)
35 {
36    unsigned long long v;
37 
38    v = cs->mult;
39    /*
40     * We want an even value to automatically clear the top bit
41     * returned by cnt32_to_63() without an additional run time
42     * instruction. So if the LSB is 1 then round it up.
43     */
44    if (v & 1)
45        v++;
46    tclk2ns_scale = v;
47    tclk2ns_scale_factor = cs->shift;
48 }
49 
50 static struct timer_list __maybe_unused cnt32_to_63_keepwarm_timer;
51 
cnt32_to_63_keepwarm(unsigned long data)52 static void __maybe_unused cnt32_to_63_keepwarm(unsigned long data)
53 {
54    mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
55    (void) sched_clock();
56 }
57 
setup_sched_clock_update(unsigned long tclk)58 static void __maybe_unused setup_sched_clock_update(unsigned long tclk)
59 {
60    unsigned long data;
61 
62    data = (0xffffffffUL / tclk / 2 - 2) * HZ;
63    setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, data);
64    mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
65 }
66 
c0_hpt_read(void)67 static cycle_t c0_hpt_read(void)
68 {
69 	return read_c0_count();
70 }
71 
72 static struct clocksource clocksource_mips = {
73 	.name		= "MIPS",
74 	.read		= c0_hpt_read,
75 	.mask		= CLOCKSOURCE_MASK(32),
76 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
77 };
78 
init_r4k_clocksource(void)79 int __init init_r4k_clocksource(void)
80 {
81 	if (!cpu_has_counter || !mips_hpt_frequency)
82 		return -ENXIO;
83 
84 	/* Calculate a somewhat reasonable rating value */
85 	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
86 
87 	clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
88 
89 #ifdef CONFIG_HR_SCHED_CLOCK
90 	setup_sched_clock(&clocksource_mips);
91 #endif
92 	clocksource_register(&clocksource_mips);
93 
94 #ifdef CONFIG_HR_SCHED_CLOCK_UPDATE
95 	setup_sched_clock_update(mips_hpt_frequency);
96 #endif
97 	return 0;
98 }
99