]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright 2001 MontaVista Software Inc. | |
3 | * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net | |
4 | * Copyright (c) 2003, 2004 Maciej W. Rozycki | |
5 | * | |
d9eec1a5 | 6 | * Common time service routines for MIPS machines. |
1da177e4 LT |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | */ | |
656db506 | 13 | #include <linux/bug.h> |
7bcf7717 | 14 | #include <linux/clockchips.h> |
1da177e4 LT |
15 | #include <linux/types.h> |
16 | #include <linux/kernel.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/sched.h> | |
19 | #include <linux/param.h> | |
20 | #include <linux/time.h> | |
21 | #include <linux/timex.h> | |
22 | #include <linux/smp.h> | |
1da177e4 | 23 | #include <linux/spinlock.h> |
1da177e4 LT |
24 | #include <linux/module.h> |
25 | ||
1da177e4 LT |
26 | #include <asm/cpu-features.h> |
27 | #include <asm/div64.h> | |
ea580401 | 28 | #include <asm/smtc_ipi.h> |
1da177e4 LT |
29 | #include <asm/time.h> |
30 | ||
1da177e4 LT |
31 | /* |
32 | * forward reference | |
33 | */ | |
1da177e4 | 34 | DEFINE_SPINLOCK(rtc_lock); |
4b550488 | 35 | EXPORT_SYMBOL(rtc_lock); |
1da177e4 | 36 | |
4b550488 | 37 | int __weak rtc_mips_set_time(unsigned long sec) |
1da177e4 | 38 | { |
4b550488 | 39 | return 0; |
1da177e4 | 40 | } |
4b550488 | 41 | EXPORT_SYMBOL(rtc_mips_set_time); |
1da177e4 | 42 | |
4b550488 | 43 | int __weak rtc_mips_set_mmss(unsigned long nowtime) |
1da177e4 | 44 | { |
4b550488 | 45 | return rtc_mips_set_time(nowtime); |
1da177e4 LT |
46 | } |
47 | ||
f5ff0a28 RB |
48 | int update_persistent_clock(struct timespec now) |
49 | { | |
50 | return rtc_mips_set_mmss(now.tv_sec); | |
51 | } | |
1da177e4 | 52 | |
1da177e4 LT |
53 | /* |
54 | * High precision timer functions for a R4k-compatible timer. | |
55 | */ | |
00598560 | 56 | static cycle_t c0_hpt_read(void) |
1da177e4 LT |
57 | { |
58 | return read_c0_count(); | |
59 | } | |
60 | ||
1da177e4 | 61 | int (*mips_timer_state)(void); |
1da177e4 | 62 | |
7d12e780 | 63 | int null_perf_irq(void) |
ba339c03 RB |
64 | { |
65 | return 0; | |
66 | } | |
67 | ||
91a2fcc8 RB |
68 | EXPORT_SYMBOL(null_perf_irq); |
69 | ||
7d12e780 | 70 | int (*perf_irq)(void) = null_perf_irq; |
ba339c03 | 71 | |
ba339c03 RB |
72 | EXPORT_SYMBOL(perf_irq); |
73 | ||
1da177e4 LT |
74 | /* |
75 | * time_init() - it does the following things. | |
76 | * | |
4b550488 | 77 | * 1) plat_time_init() - |
1da177e4 LT |
78 | * a) (optional) set up RTC routines, |
79 | * b) (optional) calibrate and set the mips_hpt_frequency | |
16b7b2ac AN |
80 | * (only needed if you intended to use cpu counter as timer interrupt |
81 | * source) | |
4b550488 | 82 | * 2) calculate a couple of cached variables for later usage |
1da177e4 LT |
83 | */ |
84 | ||
1da177e4 LT |
85 | unsigned int mips_hpt_frequency; |
86 | ||
d9eec1a5 AN |
87 | static struct clocksource clocksource_mips = { |
88 | .name = "MIPS", | |
89 | .read = c0_hpt_read, | |
90 | .mask = CLOCKSOURCE_MASK(32), | |
91 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
92 | }; | |
93 | ||
1da177e4 LT |
94 | static unsigned int __init calibrate_hpt(void) |
95 | { | |
00598560 | 96 | cycle_t frequency, hpt_start, hpt_end, hpt_count, hz; |
1da177e4 LT |
97 | |
98 | const int loops = HZ / 10; | |
99 | int log_2_loops = 0; | |
100 | int i; | |
101 | ||
102 | /* | |
103 | * We want to calibrate for 0.1s, but to avoid a 64-bit | |
104 | * division we round the number of loops up to the nearest | |
105 | * power of 2. | |
106 | */ | |
107 | while (loops > 1 << log_2_loops) | |
108 | log_2_loops++; | |
109 | i = 1 << log_2_loops; | |
110 | ||
111 | /* | |
112 | * Wait for a rising edge of the timer interrupt. | |
113 | */ | |
114 | while (mips_timer_state()); | |
115 | while (!mips_timer_state()); | |
116 | ||
117 | /* | |
118 | * Now see how many high precision timer ticks happen | |
119 | * during the calculated number of periods between timer | |
120 | * interrupts. | |
121 | */ | |
00598560 | 122 | hpt_start = clocksource_mips.read(); |
1da177e4 LT |
123 | do { |
124 | while (mips_timer_state()); | |
125 | while (!mips_timer_state()); | |
126 | } while (--i); | |
00598560 | 127 | hpt_end = clocksource_mips.read(); |
1da177e4 | 128 | |
00598560 | 129 | hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask; |
1da177e4 | 130 | hz = HZ; |
00598560 | 131 | frequency = hpt_count * hz; |
1da177e4 LT |
132 | |
133 | return frequency >> log_2_loops; | |
134 | } | |
135 | ||
93c846f9 | 136 | void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) |
16b7b2ac AN |
137 | { |
138 | u64 temp; | |
139 | u32 shift; | |
140 | ||
93c846f9 RB |
141 | /* Find a shift value */ |
142 | for (shift = 32; shift > 0; shift--) { | |
143 | temp = (u64) NSEC_PER_SEC << shift; | |
144 | do_div(temp, clock); | |
145 | if ((temp >> 32) == 0) | |
146 | break; | |
147 | } | |
148 | cs->shift = shift; | |
149 | cs->mult = (u32) temp; | |
150 | } | |
151 | ||
152 | void __cpuinit clockevent_set_clock(struct clock_event_device *cd, | |
153 | unsigned int clock) | |
154 | { | |
155 | u64 temp; | |
156 | u32 shift; | |
16b7b2ac | 157 | |
16b7b2ac AN |
158 | /* Find a shift value */ |
159 | for (shift = 32; shift > 0; shift--) { | |
508a775a AN |
160 | temp = (u64) clock << shift; |
161 | do_div(temp, NSEC_PER_SEC); | |
16b7b2ac AN |
162 | if ((temp >> 32) == 0) |
163 | break; | |
164 | } | |
93c846f9 RB |
165 | cd->shift = shift; |
166 | cd->mult = (u32) temp; | |
167 | } | |
168 | ||
169 | static void __init init_mips_clocksource(void) | |
170 | { | |
93c846f9 RB |
171 | /* Calclate a somewhat reasonable rating value */ |
172 | clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000; | |
173 | ||
174 | clocksource_set_clock(&clocksource_mips, mips_hpt_frequency); | |
16b7b2ac AN |
175 | |
176 | clocksource_register(&clocksource_mips); | |
177 | } | |
178 | ||
4b550488 | 179 | void __init __weak plat_time_init(void) |
1da177e4 | 180 | { |
4b550488 | 181 | } |
1da177e4 | 182 | |
656db506 RB |
183 | /* |
184 | * This function exists in order to cause an error due to a duplicate | |
185 | * definition if platform code should have its own implementation. The hook | |
186 | * to use instead is plat_time_init. plat_time_init does not receive the | |
187 | * irqaction pointer argument anymore. This is because any function which | |
188 | * initializes an interrupt timer now takes care of its own request_irq rsp. | |
189 | * setup_irq calls and each clock_event_device should use its own | |
190 | * struct irqrequest. | |
191 | */ | |
d9eec1a5 | 192 | void __init plat_timer_setup(void) |
7bcf7717 | 193 | { |
656db506 | 194 | BUG(); |
7bcf7717 RB |
195 | } |
196 | ||
4b550488 RB |
197 | void __init time_init(void) |
198 | { | |
199 | plat_time_init(); | |
1da177e4 | 200 | |
d9eec1a5 | 201 | if (cpu_has_counter && (mips_hpt_frequency || mips_timer_state)) { |
1da177e4 | 202 | /* We know counter frequency. Or we can get it. */ |
1da177e4 LT |
203 | if (!mips_hpt_frequency) |
204 | mips_hpt_frequency = calibrate_hpt(); | |
205 | ||
1da177e4 LT |
206 | /* Report the high precision timer rate for a reference. */ |
207 | printk("Using %u.%03u MHz high precision timer.\n", | |
208 | ((mips_hpt_frequency + 500) / 1000) / 1000, | |
209 | ((mips_hpt_frequency + 500) / 1000) % 1000); | |
d9eec1a5 | 210 | init_mips_clocksource(); |
1da177e4 LT |
211 | } |
212 | ||
7bcf7717 | 213 | mips_clockevent_init(); |
1da177e4 | 214 | } |