diff options
-rw-r--r-- | arch/parisc/Kconfig | 4 | ||||
-rw-r--r-- | arch/parisc/kernel/time.c | 134 |
2 files changed, 28 insertions, 110 deletions
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 848a67a024b2..e18eeecae850 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig | |||
@@ -50,6 +50,10 @@ config GENERIC_CALIBRATE_DELAY | |||
50 | bool | 50 | bool |
51 | default y | 51 | default y |
52 | 52 | ||
53 | config GENERIC_TIME | ||
54 | bool | ||
55 | default y | ||
56 | |||
53 | config TIME_LOW_RES | 57 | config TIME_LOW_RES |
54 | bool | 58 | bool |
55 | depends on SMP | 59 | depends on SMP |
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c index bad7d1eb62b9..e47e27cea42e 100644 --- a/arch/parisc/kernel/time.c +++ b/arch/parisc/kernel/time.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/smp.h> | 23 | #include <linux/smp.h> |
24 | #include <linux/profile.h> | 24 | #include <linux/profile.h> |
25 | #include <linux/clocksource.h> | ||
25 | 26 | ||
26 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
27 | #include <asm/io.h> | 28 | #include <asm/io.h> |
@@ -172,121 +173,23 @@ unsigned long profile_pc(struct pt_regs *regs) | |||
172 | EXPORT_SYMBOL(profile_pc); | 173 | EXPORT_SYMBOL(profile_pc); |
173 | 174 | ||
174 | 175 | ||
175 | /* | 176 | /* clock source code */ |
176 | * Return the number of micro-seconds that elapsed since the last | ||
177 | * update to wall time (aka xtime). The xtime_lock | ||
178 | * must be at least read-locked when calling this routine. | ||
179 | */ | ||
180 | static inline unsigned long gettimeoffset (void) | ||
181 | { | ||
182 | #ifndef CONFIG_SMP | ||
183 | /* | ||
184 | * FIXME: This won't work on smp because jiffies are updated by cpu 0. | ||
185 | * Once parisc-linux learns the cr16 difference between processors, | ||
186 | * this could be made to work. | ||
187 | */ | ||
188 | unsigned long now; | ||
189 | unsigned long prev_tick; | ||
190 | unsigned long next_tick; | ||
191 | unsigned long elapsed_cycles; | ||
192 | unsigned long usec; | ||
193 | unsigned long cpuid = smp_processor_id(); | ||
194 | unsigned long cpt = clocktick; | ||
195 | 177 | ||
196 | next_tick = cpu_data[cpuid].it_value; | 178 | static cycle_t read_cr16(void) |
197 | now = mfctl(16); /* Read the hardware interval timer. */ | ||
198 | |||
199 | prev_tick = next_tick - cpt; | ||
200 | |||
201 | /* Assume Scenario 1: "now" is later than prev_tick. */ | ||
202 | elapsed_cycles = now - prev_tick; | ||
203 | |||
204 | /* aproximate HZ with shifts. Intended math is "(elapsed/clocktick) > HZ" */ | ||
205 | #if HZ == 1000 | ||
206 | if (elapsed_cycles > (cpt << 10) ) | ||
207 | #elif HZ == 250 | ||
208 | if (elapsed_cycles > (cpt << 8) ) | ||
209 | #elif HZ == 100 | ||
210 | if (elapsed_cycles > (cpt << 7) ) | ||
211 | #else | ||
212 | #warn WTF is HZ set to anyway? | ||
213 | if (elapsed_cycles > (HZ * cpt) ) | ||
214 | #endif | ||
215 | { | ||
216 | /* Scenario 3: clock ticks are missing. */ | ||
217 | printk (KERN_CRIT "gettimeoffset(CPU %ld): missing %ld ticks!" | ||
218 | " cycles %lX prev/now/next %lX/%lX/%lX clock %lX\n", | ||
219 | cpuid, elapsed_cycles / cpt, | ||
220 | elapsed_cycles, prev_tick, now, next_tick, cpt); | ||
221 | } | ||
222 | |||
223 | /* FIXME: Can we improve the precision? Not with PAGE0. */ | ||
224 | usec = (elapsed_cycles * 10000) / PAGE0->mem_10msec; | ||
225 | return usec; | ||
226 | #else | ||
227 | return 0; | ||
228 | #endif | ||
229 | } | ||
230 | |||
231 | void | ||
232 | do_gettimeofday (struct timeval *tv) | ||
233 | { | 179 | { |
234 | unsigned long flags, seq, usec, sec; | 180 | return get_cycles(); |
235 | |||
236 | /* Hold xtime_lock and adjust timeval. */ | ||
237 | do { | ||
238 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
239 | usec = gettimeoffset(); | ||
240 | sec = xtime.tv_sec; | ||
241 | usec += (xtime.tv_nsec / 1000); | ||
242 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | ||
243 | |||
244 | /* Move adjusted usec's into sec's. */ | ||
245 | while (usec >= USEC_PER_SEC) { | ||
246 | usec -= USEC_PER_SEC; | ||
247 | ++sec; | ||
248 | } | ||
249 | |||
250 | /* Return adjusted result. */ | ||
251 | tv->tv_sec = sec; | ||
252 | tv->tv_usec = usec; | ||
253 | } | 181 | } |
254 | 182 | ||
255 | EXPORT_SYMBOL(do_gettimeofday); | 183 | static struct clocksource clocksource_cr16 = { |
256 | 184 | .name = "cr16", | |
257 | int | 185 | .rating = 300, |
258 | do_settimeofday (struct timespec *tv) | 186 | .read = read_cr16, |
259 | { | 187 | .mask = CLOCKSOURCE_MASK(BITS_PER_LONG), |
260 | time_t wtm_sec, sec = tv->tv_sec; | 188 | .mult = 0, /* to be set */ |
261 | long wtm_nsec, nsec = tv->tv_nsec; | 189 | .shift = 22, |
262 | 190 | .is_continuous = 1, | |
263 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | 191 | }; |
264 | return -EINVAL; | ||
265 | |||
266 | write_seqlock_irq(&xtime_lock); | ||
267 | { | ||
268 | /* | ||
269 | * This is revolting. We need to set "xtime" | ||
270 | * correctly. However, the value in this location is | ||
271 | * the value at the most recent update of wall time. | ||
272 | * Discover what correction gettimeofday would have | ||
273 | * done, and then undo it! | ||
274 | */ | ||
275 | nsec -= gettimeoffset() * 1000; | ||
276 | 192 | ||
277 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
278 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
279 | |||
280 | set_normalized_timespec(&xtime, sec, nsec); | ||
281 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
282 | |||
283 | ntp_clear(); | ||
284 | } | ||
285 | write_sequnlock_irq(&xtime_lock); | ||
286 | clock_was_set(); | ||
287 | return 0; | ||
288 | } | ||
289 | EXPORT_SYMBOL(do_settimeofday); | ||
290 | 193 | ||
291 | /* | 194 | /* |
292 | * XXX: We can do better than this. | 195 | * XXX: We can do better than this. |
@@ -312,11 +215,22 @@ void __init start_cpu_itimer(void) | |||
312 | void __init time_init(void) | 215 | void __init time_init(void) |
313 | { | 216 | { |
314 | static struct pdc_tod tod_data; | 217 | static struct pdc_tod tod_data; |
218 | unsigned long current_cr16_khz; | ||
315 | 219 | ||
316 | clocktick = (100 * PAGE0->mem_10msec) / HZ; | 220 | clocktick = (100 * PAGE0->mem_10msec) / HZ; |
317 | 221 | ||
318 | start_cpu_itimer(); /* get CPU 0 started */ | 222 | start_cpu_itimer(); /* get CPU 0 started */ |
319 | 223 | ||
224 | /* register at clocksource framework */ | ||
225 | current_cr16_khz = PAGE0->mem_10msec/10; /* kHz */ | ||
226 | clocksource_cr16.mult = clocksource_khz2mult(current_cr16_khz, | ||
227 | clocksource_cr16.shift); | ||
228 | /* lower the rating if we already know its unstable: */ | ||
229 | if (num_online_cpus()>1) | ||
230 | clocksource_cr16.rating = 200; | ||
231 | |||
232 | clocksource_register(&clocksource_cr16); | ||
233 | |||
320 | if (pdc_tod_read(&tod_data) == 0) { | 234 | if (pdc_tod_read(&tod_data) == 0) { |
321 | unsigned long flags; | 235 | unsigned long flags; |
322 | 236 | ||