aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc/kernel/time.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-26 15:48:06 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-26 15:48:06 -0500
commitb0138a6cb7923a997d278b47c176778534d1095b (patch)
tree4fcb8822a69631baba568e4e1942847747123887 /arch/parisc/kernel/time.c
parent6572d6d7d0f965dda19d02af804ed3ae4b3bf1fc (diff)
parent1055a8af093fea7490445bd15cd671020e542035 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6: (78 commits) [PARISC] Use symbolic last syscall in __NR_Linux_syscalls [PARISC] Add missing statfs64 and fstatfs64 syscalls Revert "[PARISC] Optimize TLB flush on SMP systems" [PARISC] Compat signal fixes for 64-bit parisc [PARISC] Reorder syscalls to match unistd.h Revert "[PATCH] make kernel/signal.c:kill_proc_info() static" [PARISC] fix sys_rt_sigqueueinfo [PARISC] fix section mismatch warnings in harmony sound driver [PARISC] do not export get_register/set_register [PARISC] add ENTRY()/ENDPROC() and simplify assembly of HP/UX emulation code [PARISC] convert to use CONFIG_64BIT instead of __LP64__ [PARISC] use CONFIG_64BIT instead of __LP64__ [PARISC] add ASM_EXCEPTIONTABLE_ENTRY() macro [PARISC] more ENTRY(), ENDPROC(), END() conversions [PARISC] fix ENTRY() and ENDPROC() for 64bit-parisc [PARISC] Fixes /proc/cpuinfo cache output on B160L [PARISC] implement standard ENTRY(), END() and ENDPROC() [PARISC] kill ENTRY_SYS_CPUS [PARISC] clean up debugging printks in smp.c [PARISC] factor syscall_restart code out of do_signal ... Fix conflict in include/linux/sched.h due to kill_proc_info() being made publicly available to PARISC again.
Diffstat (limited to 'arch/parisc/kernel/time.c')
-rw-r--r--arch/parisc/kernel/time.c146
1 files changed, 35 insertions, 111 deletions
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c
index 5f1b51af06a9..d1db8e518654 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>
@@ -98,7 +99,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id)
98 * cycles after the IT fires. But it's arbitrary how much time passes 99 * cycles after the IT fires. But it's arbitrary how much time passes
99 * before we call it "late". I've picked one second. 100 * before we call it "late". I've picked one second.
100 */ 101 */
101 if (ticks_elapsed > HZ) { 102 if (unlikely(ticks_elapsed > HZ)) {
102 /* Scenario 3: very long delay? bad in any case */ 103 /* Scenario 3: very long delay? bad in any case */
103 printk (KERN_CRIT "timer_interrupt(CPU %d): delayed!" 104 printk (KERN_CRIT "timer_interrupt(CPU %d): delayed!"
104 " cycles %lX rem %lX " 105 " cycles %lX rem %lX "
@@ -147,10 +148,6 @@ irqreturn_t timer_interrupt(int irq, void *dev_id)
147 write_sequnlock(&xtime_lock); 148 write_sequnlock(&xtime_lock);
148 } 149 }
149 150
150 /* check soft power switch status */
151 if (cpu == 0 && !atomic_read(&power_tasklet.count))
152 tasklet_schedule(&power_tasklet);
153
154 return IRQ_HANDLED; 151 return IRQ_HANDLED;
155} 152}
156 153
@@ -172,121 +169,41 @@ unsigned long profile_pc(struct pt_regs *regs)
172EXPORT_SYMBOL(profile_pc); 169EXPORT_SYMBOL(profile_pc);
173 170
174 171
175/* 172/* 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
196 next_tick = cpu_data[cpuid].it_value;
197 now = mfctl(16); /* Read the hardware interval timer. */
198 173
199 prev_tick = next_tick - cpt; 174static cycle_t read_cr16(void)
175{
176 return get_cycles();
177}
200 178
201 /* Assume Scenario 1: "now" is later than prev_tick. */ 179static int cr16_update_callback(void);
202 elapsed_cycles = now - prev_tick;
203 180
204/* aproximate HZ with shifts. Intended math is "(elapsed/clocktick) > HZ" */ 181static struct clocksource clocksource_cr16 = {
205#if HZ == 1000 182 .name = "cr16",
206 if (elapsed_cycles > (cpt << 10) ) 183 .rating = 300,
207#elif HZ == 250 184 .read = read_cr16,
208 if (elapsed_cycles > (cpt << 8) ) 185 .mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
209#elif HZ == 100 186 .mult = 0, /* to be set */
210 if (elapsed_cycles > (cpt << 7) ) 187 .shift = 22,
211#else 188 .update_callback = cr16_update_callback,
212#warn WTF is HZ set to anyway? 189 .is_continuous = 1,
213 if (elapsed_cycles > (HZ * cpt) ) 190};
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 191
223 /* FIXME: Can we improve the precision? Not with PAGE0. */ 192static int cr16_update_callback(void)
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{ 193{
234 unsigned long flags, seq, usec, sec; 194 int change = 0;
235 195
236 /* Hold xtime_lock and adjust timeval. */ 196 /* since the cr16 cycle counters are not syncronized across CPUs,
237 do { 197 we'll check if we should switch to a safe clocksource: */
238 seq = read_seqbegin_irqsave(&xtime_lock, flags); 198 if (clocksource_cr16.rating != 0 && num_online_cpus() > 1) {
239 usec = gettimeoffset(); 199 clocksource_cr16.rating = 0;
240 sec = xtime.tv_sec; 200 clocksource_reselect();
241 usec += (xtime.tv_nsec / 1000); 201 change = 1;
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 } 202 }
249 203
250 /* Return adjusted result. */ 204 return change;
251 tv->tv_sec = sec;
252 tv->tv_usec = usec;
253} 205}
254 206
255EXPORT_SYMBOL(do_gettimeofday);
256
257int
258do_settimeofday (struct timespec *tv)
259{
260 time_t wtm_sec, sec = tv->tv_sec;
261 long wtm_nsec, nsec = tv->tv_nsec;
262
263 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
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
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 207
291void __init start_cpu_itimer(void) 208void __init start_cpu_itimer(void)
292{ 209{
@@ -301,11 +218,18 @@ void __init start_cpu_itimer(void)
301void __init time_init(void) 218void __init time_init(void)
302{ 219{
303 static struct pdc_tod tod_data; 220 static struct pdc_tod tod_data;
221 unsigned long current_cr16_khz;
304 222
305 clocktick = (100 * PAGE0->mem_10msec) / HZ; 223 clocktick = (100 * PAGE0->mem_10msec) / HZ;
306 224
307 start_cpu_itimer(); /* get CPU 0 started */ 225 start_cpu_itimer(); /* get CPU 0 started */
308 226
227 /* register at clocksource framework */
228 current_cr16_khz = PAGE0->mem_10msec/10; /* kHz */
229 clocksource_cr16.mult = clocksource_khz2mult(current_cr16_khz,
230 clocksource_cr16.shift);
231 clocksource_register(&clocksource_cr16);
232
309 if (pdc_tod_read(&tod_data) == 0) { 233 if (pdc_tod_read(&tod_data) == 0) {
310 unsigned long flags; 234 unsigned long flags;
311 235