aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc/kernel/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/parisc/kernel/time.c')
-rw-r--r--arch/parisc/kernel/time.c148
1 files changed, 37 insertions, 111 deletions
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c
index 5f1b51af06a9..07a991aa9b0c 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,43 @@ 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
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 173
231void 174static cycle_t read_cr16(void)
232do_gettimeofday (struct timeval *tv)
233{ 175{
234 unsigned long flags, seq, usec, sec; 176 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} 177}
254 178
255EXPORT_SYMBOL(do_gettimeofday); 179static struct clocksource clocksource_cr16 = {
180 .name = "cr16",
181 .rating = 300,
182 .read = read_cr16,
183 .mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
184 .mult = 0, /* to be set */
185 .shift = 22,
186 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
187};
256 188
257int 189#ifdef CONFIG_SMP
258do_settimeofday (struct timespec *tv) 190int update_cr16_clocksource(void)
259{ 191{
260 time_t wtm_sec, sec = tv->tv_sec; 192 int change = 0;
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 193
277 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); 194 /* since the cr16 cycle counters are not syncronized across CPUs,
278 wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); 195 we'll check if we should switch to a safe clocksource: */
279 196 if (clocksource_cr16.rating != 0 && num_online_cpus() > 1) {
280 set_normalized_timespec(&xtime, sec, nsec); 197 clocksource_change_rating(&clocksource_cr16, 0);
281 set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); 198 change = 1;
282
283 ntp_clear();
284 } 199 }
285 write_sequnlock_irq(&xtime_lock); 200
286 clock_was_set(); 201 return change;
287 return 0;
288} 202}
289EXPORT_SYMBOL(do_settimeofday); 203#else
204int update_cr16_clocksource(void)
205{
206 return 0; /* no change */
207}
208#endif /*CONFIG_SMP*/
290 209
291void __init start_cpu_itimer(void) 210void __init start_cpu_itimer(void)
292{ 211{
@@ -301,11 +220,18 @@ void __init start_cpu_itimer(void)
301void __init time_init(void) 220void __init time_init(void)
302{ 221{
303 static struct pdc_tod tod_data; 222 static struct pdc_tod tod_data;
223 unsigned long current_cr16_khz;
304 224
305 clocktick = (100 * PAGE0->mem_10msec) / HZ; 225 clocktick = (100 * PAGE0->mem_10msec) / HZ;
306 226
307 start_cpu_itimer(); /* get CPU 0 started */ 227 start_cpu_itimer(); /* get CPU 0 started */
308 228
229 /* register at clocksource framework */
230 current_cr16_khz = PAGE0->mem_10msec/10; /* kHz */
231 clocksource_cr16.mult = clocksource_khz2mult(current_cr16_khz,
232 clocksource_cr16.shift);
233 clocksource_register(&clocksource_cr16);
234
309 if (pdc_tod_read(&tod_data) == 0) { 235 if (pdc_tod_read(&tod_data) == 0) {
310 unsigned long flags; 236 unsigned long flags;
311 237