diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-26 15:48:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-26 15:48:06 -0500 |
commit | b0138a6cb7923a997d278b47c176778534d1095b (patch) | |
tree | 4fcb8822a69631baba568e4e1942847747123887 /arch/parisc/kernel/time.c | |
parent | 6572d6d7d0f965dda19d02af804ed3ae4b3bf1fc (diff) | |
parent | 1055a8af093fea7490445bd15cd671020e542035 (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.c | 146 |
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) | |||
172 | EXPORT_SYMBOL(profile_pc); | 169 | EXPORT_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 | */ | ||
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 | |||
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; | 174 | static cycle_t read_cr16(void) |
175 | { | ||
176 | return get_cycles(); | ||
177 | } | ||
200 | 178 | ||
201 | /* Assume Scenario 1: "now" is later than prev_tick. */ | 179 | static int cr16_update_callback(void); |
202 | elapsed_cycles = now - prev_tick; | ||
203 | 180 | ||
204 | /* aproximate HZ with shifts. Intended math is "(elapsed/clocktick) > HZ" */ | 181 | static 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. */ | 192 | static 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 | |||
231 | void | ||
232 | do_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 | ||
255 | EXPORT_SYMBOL(do_gettimeofday); | ||
256 | |||
257 | int | ||
258 | do_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 | } | ||
289 | EXPORT_SYMBOL(do_settimeofday); | ||
290 | 207 | ||
291 | void __init start_cpu_itimer(void) | 208 | void __init start_cpu_itimer(void) |
292 | { | 209 | { |
@@ -301,11 +218,18 @@ void __init start_cpu_itimer(void) | |||
301 | void __init time_init(void) | 218 | void __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 | ||