time.c 11.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 * linux/arch/ia64/kernel/time.c
 *
4
 * Copyright (C) 1998-2003 Hewlett-Packard Co
5 6
 *	Stephane Eranian <eranian@hpl.hp.com>
 *	David Mosberger <davidm@hpl.hp.com>
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14
 * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
 * Copyright (C) 1999-2000 VA Linux Systems
 * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
 */
#include <linux/config.h>

#include <linux/init.h>
#include <linux/kernel.h>
15
#include <linux/module.h>
16
#include <linux/profile.h>
Linus Torvalds's avatar
Linus Torvalds committed
17 18 19
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/interrupt.h>
20
#include <linux/efi.h>
Will Cohen's avatar
Will Cohen committed
21
#include <linux/profile.h>
22
#include <linux/timex.h>
Linus Torvalds's avatar
Linus Torvalds committed
23

24
#include <asm/machvec.h>
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28
#include <asm/delay.h>
#include <asm/hw_irq.h>
#include <asm/ptrace.h>
#include <asm/sal.h>
29
#include <asm/sections.h>
Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33
#include <asm/system.h>

extern unsigned long wall_jiffies;

34
u64 jiffies_64 = INITIAL_JIFFIES;
35

36 37
EXPORT_SYMBOL(jiffies_64);

38 39
#define TIME_KEEPER_ID	0	/* smp_processor_id() of time-keeper */

Linus Torvalds's avatar
Linus Torvalds committed
40 41 42
#ifdef CONFIG_IA64_DEBUG_IRQ

unsigned long last_cli_ip;
David Mosberger's avatar
David Mosberger committed
43
EXPORT_SYMBOL(last_cli_ip);
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46

#endif

47 48 49 50 51 52 53 54
unsigned long long
sched_clock (void)
{
	unsigned long offset = ia64_get_itc();

	return (offset * local_cpu_data->nsec_per_cyc) >> IA64_NSEC_PER_CYC_SHIFT;
}

55 56
static void
itc_reset (void)
57 58 59 60 61 62 63
{
}

/*
 * Adjust for the fact that xtime has been advanced by delta_nsec (may be negative and/or
 * larger than NSEC_PER_SEC.
 */
64 65
static void
itc_update (long delta_nsec)
66 67 68
{
}

Linus Torvalds's avatar
Linus Torvalds committed
69
/*
70 71 72 73 74 75
 * Return the number of nano-seconds that elapsed since the last
 * update to jiffy.  It is quite possible that the timer interrupt
 * will interrupt this and result in a race for any of jiffies,
 * wall_jiffies or itm_next.  Thus, the xtime_lock must be at least
 * read synchronised when calling this routine (see do_gettimeofday()
 * below for an example).
Linus Torvalds's avatar
Linus Torvalds committed
76
 */
77
unsigned long
78
itc_get_offset (void)
Linus Torvalds's avatar
Linus Torvalds committed
79 80
{
	unsigned long elapsed_cycles, lost = jiffies - wall_jiffies;
81
	unsigned long now = ia64_get_itc(), last_tick;
Linus Torvalds's avatar
Linus Torvalds committed
82

83 84
	last_tick = (cpu_data(TIME_KEEPER_ID)->itm_next
		     - (lost + 1)*cpu_data(TIME_KEEPER_ID)->itm_delta);
Linus Torvalds's avatar
Linus Torvalds committed
85

Linus Torvalds's avatar
Linus Torvalds committed
86
	elapsed_cycles = now - last_tick;
David Mosberger's avatar
David Mosberger committed
87
	return (elapsed_cycles*local_cpu_data->nsec_per_cyc) >> IA64_NSEC_PER_CYC_SHIFT;
Linus Torvalds's avatar
Linus Torvalds committed
88 89
}

90 91 92 93 94 95
static struct time_interpolator itc_interpolator = {
	.get_offset =	itc_get_offset,
	.update =	itc_update,
	.reset =	itc_reset
};

96 97
int
do_settimeofday (struct timespec *tv)
Linus Torvalds's avatar
Linus Torvalds committed
98
{
99
	time_t wtm_sec, sec = tv->tv_sec;
100 101 102 103
	long wtm_nsec, nsec = tv->tv_nsec;

	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
		return -EINVAL;
104

105
	write_seqlock_irq(&xtime_lock);
Linus Torvalds's avatar
Linus Torvalds committed
106 107
	{
		/*
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111
		 * This is revolting. We need to set "xtime" correctly. However, the value
		 * in this location is the value at the most recent update of wall time.
		 * Discover what correction gettimeofday would have done, and then undo
		 * it!
Linus Torvalds's avatar
Linus Torvalds committed
112
		 */
113
		nsec -= time_interpolator_get_offset();
Linus Torvalds's avatar
Linus Torvalds committed
114

115 116 117 118 119
		wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
		wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);

		set_normalized_timespec(&xtime, sec, nsec);
		set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
Linus Torvalds's avatar
Linus Torvalds committed
120 121 122 123 124

		time_adjust = 0;		/* stop active adjtime() */
		time_status |= STA_UNSYNC;
		time_maxerror = NTP_PHASE_LIMIT;
		time_esterror = NTP_PHASE_LIMIT;
125
		time_interpolator_reset();
Linus Torvalds's avatar
Linus Torvalds committed
126
	}
127
	write_sequnlock_irq(&xtime_lock);
Eric Piel's avatar
Eric Piel committed
128
	clock_was_set();
129
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
130 131
}

132 133
EXPORT_SYMBOL(do_settimeofday);

Linus Torvalds's avatar
Linus Torvalds committed
134 135 136
void
do_gettimeofday (struct timeval *tv)
{
137
	unsigned long seq, nsec, usec, sec, old, offset;
Linus Torvalds's avatar
Linus Torvalds committed
138

David Mosberger's avatar
David Mosberger committed
139
	while (1) {
140
		seq = read_seqbegin(&xtime_lock);
141 142
		{
			old = last_nsec_offset;
143
			offset = time_interpolator_get_offset();
144 145 146 147 148
			sec = xtime.tv_sec;
			nsec = xtime.tv_nsec;
		}
		if (unlikely(read_seqretry(&xtime_lock, seq)))
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
149
		/*
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
		 * Ensure that for any pair of causally ordered gettimeofday() calls, time
		 * never goes backwards (even when ITC on different CPUs are not perfectly
		 * synchronized).  (A pair of concurrent calls to gettimeofday() is by
		 * definition non-causal and hence it makes no sense to talk about
		 * time-continuity for such calls.)
		 *
		 * Doing this in a lock-free and race-free manner is tricky.  Here is why
		 * it works (most of the time): read_seqretry() just succeeded, which
		 * implies we calculated a consistent (valid) value for "offset".  If the
		 * cmpxchg() below succeeds, we further know that last_nsec_offset still
		 * has the same value as at the beginning of the loop, so there was
		 * presumably no timer-tick or other updates to last_nsec_offset in the
		 * meantime.  This isn't 100% true though: there _is_ a possibility of a
		 * timer-tick occurring right right after read_seqretry() and then getting
		 * zero or more other readers which will set last_nsec_offset to the same
		 * value as the one we read at the beginning of the loop.  If this
		 * happens, we'll end up returning a slightly newer time than we ought to
		 * (the jump forward is at most "offset" nano-seconds).  There is no
		 * danger of causing time to go backwards, though, so we are safe in that
		 * sense.  We could make the probability of this unlucky case occurring
		 * arbitrarily small by encoding a version number in last_nsec_offset, but
		 * even without versioning, the probability of this unlucky case should be
		 * so small that we won't worry about it.
Linus Torvalds's avatar
Linus Torvalds committed
173
		 */
174 175
		if (offset <= old) {
			offset = old;
David Mosberger's avatar
David Mosberger committed
176
			break;
177 178
		} else if (likely(cmpxchg(&last_nsec_offset, old, offset) == old))
			break;
Linus Torvalds's avatar
Linus Torvalds committed
179

180
		/* someone else beat us to updating last_nsec_offset; try again */
David Mosberger's avatar
David Mosberger committed
181
	}
182

183
	usec = (nsec + offset) / 1000;
Linus Torvalds's avatar
Linus Torvalds committed
184

185 186
	while (unlikely(usec >= USEC_PER_SEC)) {
		usec -= USEC_PER_SEC;
Linus Torvalds's avatar
Linus Torvalds committed
187 188 189 190 191 192 193
		++sec;
	}

	tv->tv_sec = sec;
	tv->tv_usec = usec;
}

194 195
EXPORT_SYMBOL(do_gettimeofday);

Will Cohen's avatar
Will Cohen committed
196 197 198 199 200 201 202 203 204 205
/*
 * The profiling function is SMP safe. (nothing can mess
 * around with "current", and the profiling counters are
 * updated with atomic operations). This is especially
 * useful with a profiling multiplier != 1
 */
static inline void
ia64_do_profile (struct pt_regs * regs)
{
	unsigned long ip, slot;
Jesse Barnes's avatar
Jesse Barnes committed
206
	extern cpumask_t prof_cpu_mask;
Will Cohen's avatar
Will Cohen committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

	profile_hook(regs);

	if (user_mode(regs))
		return;

	if (!prof_buffer)
		return;

	ip = instruction_pointer(regs);
	/* Conserve space in histogram by encoding slot bits in address
	 * bits 2 and 3 rather than bits 0 and 1.
	 */
	slot = ip & 3;
	ip = (ip & ~3UL) + 4*slot;

	/*
	 * Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
	 * (default is all CPUs.)
	 */
	if (!cpu_isset(smp_processor_id(), prof_cpu_mask))
		return;

	ip -= (unsigned long) &_stext;
	ip >>= prof_shift;
	/*
	 * Don't ignore out-of-bounds IP values silently,
	 * put them into the last histogram slot, so if
	 * present, they will show up as a sharp peak.
	 */
	if (ip > prof_len-1)
		ip = prof_len-1;
	atomic_inc((atomic_t *)&prof_buffer[ip]);
}

242 243
static irqreturn_t
timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
244 245 246
{
	unsigned long new_itm;

247 248
	platform_timer_interrupt(irq, dev_id, regs);

Linus Torvalds's avatar
Linus Torvalds committed
249
	new_itm = local_cpu_data->itm_next;
Linus Torvalds's avatar
Linus Torvalds committed
250 251

	if (!time_after(ia64_get_itc(), new_itm))
252
		printk(KERN_ERR "Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
Linus Torvalds's avatar
Linus Torvalds committed
253 254
		       ia64_get_itc(), new_itm);

Will Cohen's avatar
Will Cohen committed
255 256
	ia64_do_profile(regs);

Linus Torvalds's avatar
Linus Torvalds committed
257 258
	while (1) {
#ifdef CONFIG_SMP
259 260 261 262 263 264
		/*
		 * For UP, this is done in do_timer().  Weird, but
		 * fixing that would require updates to all
		 * platforms.
		 */
		update_process_times(user_mode(regs));
Linus Torvalds's avatar
Linus Torvalds committed
265
#endif
Linus Torvalds's avatar
Linus Torvalds committed
266 267
		new_itm += local_cpu_data->itm_delta;

268
		if (smp_processor_id() == TIME_KEEPER_ID) {
Linus Torvalds's avatar
Linus Torvalds committed
269 270 271 272 273 274
			/*
			 * Here we are in the timer irq handler. We have irqs locally
			 * disabled, but we don't know if the timer_bh is running on
			 * another CPU. We need to avoid to SMP race by acquiring the
			 * xtime_lock.
			 */
275
			write_seqlock(&xtime_lock);
Linus Torvalds's avatar
Linus Torvalds committed
276
			do_timer(regs);
Linus Torvalds's avatar
Linus Torvalds committed
277
			local_cpu_data->itm_next = new_itm;
278
			write_sequnlock(&xtime_lock);
Linus Torvalds's avatar
Linus Torvalds committed
279 280
		} else
			local_cpu_data->itm_next = new_itm;
Linus Torvalds's avatar
Linus Torvalds committed
281 282 283 284 285

		if (time_after(new_itm, ia64_get_itc()))
			break;
	}

Linus Torvalds's avatar
Linus Torvalds committed
286
	do {
287 288 289 290 291 292 293 294 295 296 297 298 299
		/*
		 * If we're too close to the next clock tick for
		 * comfort, we increase the safety margin by
		 * intentionally dropping the next tick(s).  We do NOT
		 * update itm.next because that would force us to call
		 * do_timer() which in turn would let our clock run
		 * too fast (with the potentially devastating effect
		 * of losing monotony of time).
		 */
		while (!time_after(new_itm, ia64_get_itc() + local_cpu_data->itm_delta/2))
			new_itm += local_cpu_data->itm_delta;
		ia64_set_itm(new_itm);
		/* double check, in case we got hit by a (slow) PMI: */
Linus Torvalds's avatar
Linus Torvalds committed
300
	} while (time_after_eq(ia64_get_itc(), new_itm));
301
	return IRQ_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
302 303 304 305 306
}

/*
 * Encapsulate access to the itm structure for SMP.
 */
307
void
Linus Torvalds's avatar
Linus Torvalds committed
308
ia64_cpu_local_tick (void)
Linus Torvalds's avatar
Linus Torvalds committed
309
{
Linus Torvalds's avatar
Linus Torvalds committed
310 311
	int cpu = smp_processor_id();
	unsigned long shift = 0, delta;
Linus Torvalds's avatar
Linus Torvalds committed
312 313

	/* arrange for the cycle counter to generate a timer interrupt: */
Linus Torvalds's avatar
Linus Torvalds committed
314 315 316 317 318 319 320 321 322 323 324 325 326
	ia64_set_itv(IA64_TIMER_VECTOR);

	delta = local_cpu_data->itm_delta;
	/*
	 * Stagger the timer tick for each CPU so they don't occur all at (almost) the
	 * same time:
	 */
	if (cpu) {
		unsigned long hi = 1UL << ia64_fls(cpu);
		shift = (2*(cpu - hi) + 1) * delta/hi/2;
	}
	local_cpu_data->itm_next = ia64_get_itc() + delta + shift;
	ia64_set_itm(local_cpu_data->itm_next);
Linus Torvalds's avatar
Linus Torvalds committed
327 328 329 330 331
}

void __init
ia64_init_itm (void)
{
332
	unsigned long platform_base_freq, itc_freq;
Linus Torvalds's avatar
Linus Torvalds committed
333
	struct pal_freq_ratio itc_ratio, proc_ratio;
334
	long status, platform_base_drift, itc_drift;
Linus Torvalds's avatar
Linus Torvalds committed
335 336

	/*
Linus Torvalds's avatar
Linus Torvalds committed
337 338 339
	 * According to SAL v2.6, we need to use a SAL call to determine the platform base
	 * frequency and then a PAL call to determine the frequency ratio between the ITC
	 * and the base frequency.
Linus Torvalds's avatar
Linus Torvalds committed
340
	 */
341 342
	status = ia64_sal_freq_base(SAL_FREQ_BASE_PLATFORM,
				    &platform_base_freq, &platform_base_drift);
Linus Torvalds's avatar
Linus Torvalds committed
343
	if (status != 0) {
344
		printk(KERN_ERR "SAL_FREQ_BASE_PLATFORM failed: %s\n", ia64_sal_strerror(status));
Linus Torvalds's avatar
Linus Torvalds committed
345 346 347
	} else {
		status = ia64_pal_freq_ratios(&proc_ratio, 0, &itc_ratio);
		if (status != 0)
348
			printk(KERN_ERR "PAL_FREQ_RATIOS failed with status=%ld\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351
	}
	if (status != 0) {
		/* invent "random" values */
352
		printk(KERN_ERR
353
		       "SAL/PAL failed to obtain frequency info---inventing reasonable values\n");
Linus Torvalds's avatar
Linus Torvalds committed
354
		platform_base_freq = 100000000;
355
		platform_base_drift = -1;	/* no drift info */
Linus Torvalds's avatar
Linus Torvalds committed
356 357 358 359
		itc_ratio.num = 3;
		itc_ratio.den = 1;
	}
	if (platform_base_freq < 40000000) {
360
		printk(KERN_ERR "Platform base frequency %lu bogus---resetting to 75MHz!\n",
Linus Torvalds's avatar
Linus Torvalds committed
361 362
		       platform_base_freq);
		platform_base_freq = 75000000;
363
		platform_base_drift = -1;
Linus Torvalds's avatar
Linus Torvalds committed
364 365
	}
	if (!proc_ratio.den)
Linus Torvalds's avatar
Linus Torvalds committed
366
		proc_ratio.den = 1;	/* avoid division by zero */
Linus Torvalds's avatar
Linus Torvalds committed
367
	if (!itc_ratio.den)
Linus Torvalds's avatar
Linus Torvalds committed
368
		itc_ratio.den = 1;	/* avoid division by zero */
Linus Torvalds's avatar
Linus Torvalds committed
369

Linus Torvalds's avatar
Linus Torvalds committed
370
	itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den;
371 372 373 374 375
	if (platform_base_drift != -1)
		itc_drift = platform_base_drift*itc_ratio.num/itc_ratio.den;
	else
		itc_drift = -1;

Linus Torvalds's avatar
Linus Torvalds committed
376
	local_cpu_data->itm_delta = (itc_freq + HZ/2) / HZ;
377
	printk(KERN_INFO "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, "
378
	       "ITC freq=%lu.%03luMHz+/-%ldppm\n", smp_processor_id(),
Linus Torvalds's avatar
Linus Torvalds committed
379
	       platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
380 381
	       itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000,
	       itc_drift);
Linus Torvalds's avatar
Linus Torvalds committed
382

Linus Torvalds's avatar
Linus Torvalds committed
383 384
	local_cpu_data->proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den;
	local_cpu_data->itc_freq = itc_freq;
385 386
	local_cpu_data->cyc_per_usec = (itc_freq + USEC_PER_SEC/2) / USEC_PER_SEC;
	local_cpu_data->nsec_per_cyc = ((NSEC_PER_SEC<<IA64_NSEC_PER_CYC_SHIFT)
Linus Torvalds's avatar
Linus Torvalds committed
387
					+ itc_freq/2)/itc_freq;
Linus Torvalds's avatar
Linus Torvalds committed
388

389 390 391 392 393 394
	if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
		itc_interpolator.frequency = local_cpu_data->itc_freq;
		itc_interpolator.drift = itc_drift;
		register_time_interpolator(&itc_interpolator);
	}

Linus Torvalds's avatar
Linus Torvalds committed
395 396 397 398 399
	/* Setup the CPU local timer tick */
	ia64_cpu_local_tick();
}

static struct irqaction timer_irqaction = {
400 401 402
	.handler =	timer_interrupt,
	.flags =	SA_INTERRUPT,
	.name =		"timer"
Linus Torvalds's avatar
Linus Torvalds committed
403 404 405 406 407
};

void __init
time_init (void)
{
Linus Torvalds's avatar
Linus Torvalds committed
408
	register_percpu_irq(IA64_TIMER_VECTOR, &timer_irqaction);
409
	efi_gettimeofday(&xtime);
Linus Torvalds's avatar
Linus Torvalds committed
410
	ia64_init_itm();
411 412 413 414 415 416

	/*
	 * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the
	 * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC).
	 */
	set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
Linus Torvalds's avatar
Linus Torvalds committed
417
}