aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/parisc')
-rw-r--r--arch/parisc/Kconfig4
-rw-r--r--arch/parisc/kernel/time.c134
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
53config GENERIC_TIME
54 bool
55 default y
56
53config TIME_LOW_RES 57config 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)
172EXPORT_SYMBOL(profile_pc); 173EXPORT_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 */
180static 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; 178static 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
231void
232do_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
255EXPORT_SYMBOL(do_gettimeofday); 183static struct clocksource clocksource_cr16 = {
256 184 .name = "cr16",
257int 185 .rating = 300,
258do_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}
289EXPORT_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)
312void __init time_init(void) 215void __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