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