time.c 11.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * Copyright 2001 MontaVista Software Inc.
 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
 * Copyright (c) 2003, 2004  Maciej W. Rozycki
 *
 * Common time service routines for MIPS machines. See
 * Documentation/mips/time.README.
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/param.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/module.h>

#include <asm/bootinfo.h>
R
Ralf Baechle 已提交
28
#include <asm/cache.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
#include <asm/compiler.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
#include <asm/div64.h>
#include <asm/sections.h>
#include <asm/time.h>

/*
 * The integer part of the number of usecs per jiffy is taken from tick,
 * but the fractional part is not recorded, so we calculate it using the
 * initial value of HZ.  This aids systems where tick isn't really an
 * integer (e.g. for HZ = 128).
 */
#define USECS_PER_JIFFY		TICK_SIZE
#define USECS_PER_JIFFY_FRAC	((unsigned long)(u32)((1000000ULL << 32) / HZ))

#define TICK_SIZE	(tick_nsec / 1000)

/*
 * forward reference
 */
DEFINE_SPINLOCK(rtc_lock);

/*
 * By default we provide the null RTC ops
 */
static unsigned long null_rtc_get_time(void)
{
	return mktime(2000, 1, 1, 0, 0, 0);
}

static int null_rtc_set_time(unsigned long sec)
{
	return 0;
}

65 66 67
unsigned long (*rtc_mips_get_time)(void) = null_rtc_get_time;
int (*rtc_mips_set_time)(unsigned long) = null_rtc_set_time;
int (*rtc_mips_set_mmss)(unsigned long);
L
Linus Torvalds 已提交
68 69 70


/* how many counter cycles in a jiffy */
R
Ralf Baechle 已提交
71
static unsigned long cycles_per_jiffy __read_mostly;
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84

/* expirelo is the count value for next CPU timer interrupt */
static unsigned int expirelo;


/*
 * Null timer ack for systems not needing one (e.g. i8254).
 */
static void null_timer_ack(void) { /* nothing */ }

/*
 * Null high precision timer functions for systems lacking one.
 */
85
static cycle_t null_hpt_read(void)
L
Linus Torvalds 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
{
	return 0;
}

/*
 * Timer ack for an R4k-compatible timer of a known frequency.
 */
static void c0_timer_ack(void)
{
	unsigned int count;

	/* Ack this timer interrupt and set the next one.  */
	expirelo += cycles_per_jiffy;
	write_c0_compare(expirelo);

	/* Check to see if we have missed any timer interrupts.  */
102
	while (((count = read_c0_count()) - expirelo) < 0x7fffffff) {
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110 111
		/* missed_timer_count++; */
		expirelo = count + cycles_per_jiffy;
		write_c0_compare(expirelo);
	}
}

/*
 * High precision timer functions for a R4k-compatible timer.
 */
112
static cycle_t c0_hpt_read(void)
L
Linus Torvalds 已提交
113 114 115 116 117
{
	return read_c0_count();
}

/* For use both as a high precision timer and an interrupt source.  */
118
static void __init c0_hpt_timer_init(void)
L
Linus Torvalds 已提交
119
{
120
	expirelo = read_c0_count() + cycles_per_jiffy;
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	write_c0_compare(expirelo);
}

int (*mips_timer_state)(void);
void (*mips_timer_ack)(void);

/* last time when xtime and rtc are sync'ed up */
static long last_rtc_update;

/*
 * local_timer_interrupt() does profiling and process accounting
 * on a per-CPU basis.
 *
 * In UP mode, it is invoked from the (global) timer_interrupt.
 *
 * In SMP mode, it might invoked by per-CPU timer interrupt, or
 * a broadcasted inter-processor interrupt which itself is triggered
 * by the global timer interrupt.
 */
140
void local_timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
141
{
142
	profile_tick(CPU_PROFILING);
143
	update_process_times(user_mode(get_irq_regs()));
L
Linus Torvalds 已提交
144 145 146 147 148 149
}

/*
 * High-level timer interrupt service routines.  This function
 * is set as irqaction->handler and is invoked through do_IRQ.
 */
150
irqreturn_t timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
151
{
152 153
	write_seqlock(&xtime_lock);

L
Linus Torvalds 已提交
154 155 156 157 158
	mips_timer_ack();

	/*
	 * call the generic timer interrupt handling
	 */
159
	do_timer(1);
L
Linus Torvalds 已提交
160 161 162

	/*
	 * If we have an externally synchronized Linux clock, then update
163
	 * CMOS clock accordingly every ~11 minutes. rtc_mips_set_time() has to be
L
Linus Torvalds 已提交
164 165
	 * called as close as possible to 500 ms before the new second starts.
	 */
J
john stultz 已提交
166
	if (ntp_synced() &&
L
Linus Torvalds 已提交
167 168 169
	    xtime.tv_sec > last_rtc_update + 660 &&
	    (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
	    (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
170
		if (rtc_mips_set_mmss(xtime.tv_sec) == 0) {
L
Linus Torvalds 已提交
171 172 173 174 175 176 177
			last_rtc_update = xtime.tv_sec;
		} else {
			/* do it again in 60 s */
			last_rtc_update = xtime.tv_sec - 600;
		}
	}

178 179
	write_sequnlock(&xtime_lock);

L
Linus Torvalds 已提交
180 181 182 183 184 185 186
	/*
	 * In UP mode, we call local_timer_interrupt() to do profiling
	 * and process accouting.
	 *
	 * In SMP mode, local_timer_interrupt() is invoked by appropriate
	 * low-level local timer interrupt handler.
	 */
187
	local_timer_interrupt(irq, dev_id);
L
Linus Torvalds 已提交
188 189 190 191

	return IRQ_HANDLED;
}

192
int null_perf_irq(void)
193 194 195 196
{
	return 0;
}

197
int (*perf_irq)(void) = null_perf_irq;
198 199 200 201

EXPORT_SYMBOL(null_perf_irq);
EXPORT_SYMBOL(perf_irq);

202
asmlinkage void ll_timer_interrupt(int irq)
L
Linus Torvalds 已提交
203
{
204 205
	int r2 = cpu_has_mips_r2;

L
Linus Torvalds 已提交
206 207 208
	irq_enter();
	kstat_this_cpu.irqs[irq]++;

209 210 211 212 213 214 215
	/*
	 * Suckage alert:
	 * Before R2 of the architecture there was no way to see if a
	 * performance counter interrupt was pending, so we have to run the
	 * performance counter interrupt handler anyway.
	 */
	if (!r2 || (read_c0_cause() & (1 << 26)))
216
		if (perf_irq())
217 218
			goto out;

L
Linus Torvalds 已提交
219
	/* we keep interrupt disabled all the time */
220
	if (!r2 || (read_c0_cause() & (1 << 30)))
221
		timer_interrupt(irq, NULL);
L
Linus Torvalds 已提交
222

223
out:
L
Linus Torvalds 已提交
224 225 226
	irq_exit();
}

227
asmlinkage void ll_local_timer_interrupt(int irq)
L
Linus Torvalds 已提交
228 229 230 231 232 233
{
	irq_enter();
	if (smp_processor_id() != 0)
		kstat_this_cpu.irqs[irq]++;

	/* we keep interrupt disabled all the time */
234
	local_timer_interrupt(irq, NULL);
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244

	irq_exit();
}

/*
 * time_init() - it does the following things.
 *
 * 1) board_time_init() -
 * 	a) (optional) set up RTC routines,
 *      b) (optional) calibrate and set the mips_hpt_frequency
245 246
 *	    (only needed if you intended to use cpu counter as timer interrupt
 *	     source)
247
 * 2) setup xtime based on rtc_mips_get_time().
248 249
 * 3) calculate a couple of cached variables for later usage
 * 4) plat_timer_setup() -
L
Linus Torvalds 已提交
250 251 252 253 254 255 256 257 258 259 260
 *	a) (optional) over-write any choices made above by time_init().
 *	b) machine specific code should setup the timer irqaction.
 *	c) enable the timer interrupt
 */

void (*board_time_init)(void);

unsigned int mips_hpt_frequency;

static struct irqaction timer_irqaction = {
	.handler = timer_interrupt,
261
	.flags = IRQF_DISABLED,
L
Linus Torvalds 已提交
262 263 264 265 266
	.name = "timer",
};

static unsigned int __init calibrate_hpt(void)
{
267
	cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
L
Linus Torvalds 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292

	const int loops = HZ / 10;
	int log_2_loops = 0;
	int i;

	/*
	 * We want to calibrate for 0.1s, but to avoid a 64-bit
	 * division we round the number of loops up to the nearest
	 * power of 2.
	 */
	while (loops > 1 << log_2_loops)
		log_2_loops++;
	i = 1 << log_2_loops;

	/*
	 * Wait for a rising edge of the timer interrupt.
	 */
	while (mips_timer_state());
	while (!mips_timer_state());

	/*
	 * Now see how many high precision timer ticks happen
	 * during the calculated number of periods between timer
	 * interrupts.
	 */
293
	hpt_start = clocksource_mips.read();
L
Linus Torvalds 已提交
294 295 296 297
	do {
		while (mips_timer_state());
		while (!mips_timer_state());
	} while (--i);
298
	hpt_end = clocksource_mips.read();
L
Linus Torvalds 已提交
299

300
	hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask;
L
Linus Torvalds 已提交
301
	hz = HZ;
302
	frequency = hpt_count * hz;
L
Linus Torvalds 已提交
303 304 305 306

	return frequency >> log_2_loops;
}

307
struct clocksource clocksource_mips = {
308
	.name		= "MIPS",
309
	.mask		= 0xffffffff,
310
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
311 312 313 314 315 316 317
};

static void __init init_mips_clocksource(void)
{
	u64 temp;
	u32 shift;

318
	if (!mips_hpt_frequency || clocksource_mips.read == null_hpt_read)
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
		return;

	/* Calclate a somewhat reasonable rating value */
	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
	/* Find a shift value */
	for (shift = 32; shift > 0; shift--) {
		temp = (u64) NSEC_PER_SEC << shift;
		do_div(temp, mips_hpt_frequency);
		if ((temp >> 32) == 0)
			break;
	}
	clocksource_mips.shift = shift;
	clocksource_mips.mult = (u32)temp;

	clocksource_register(&clocksource_mips);
}

L
Linus Torvalds 已提交
336 337 338 339 340
void __init time_init(void)
{
	if (board_time_init)
		board_time_init();

341 342
	if (!rtc_mips_set_mmss)
		rtc_mips_set_mmss = rtc_mips_set_time;
L
Linus Torvalds 已提交
343

344
	xtime.tv_sec = rtc_mips_get_time();
L
Linus Torvalds 已提交
345 346 347 348 349 350
	xtime.tv_nsec = 0;

	set_normalized_timespec(&wall_to_monotonic,
	                        -xtime.tv_sec, -xtime.tv_nsec);

	/* Choose appropriate high precision timer routines.  */
351
	if (!cpu_has_counter && !clocksource_mips.read)
L
Linus Torvalds 已提交
352
		/* No high precision timer -- sorry.  */
353
		clocksource_mips.read = null_hpt_read;
354
	else if (!mips_hpt_frequency && !mips_timer_state) {
L
Linus Torvalds 已提交
355
		/* A high precision timer of unknown frequency.  */
356
		if (!clocksource_mips.read)
L
Linus Torvalds 已提交
357
			/* No external high precision timer -- use R4k.  */
358
			clocksource_mips.read = c0_hpt_read;
L
Linus Torvalds 已提交
359 360
	} else {
		/* We know counter frequency.  Or we can get it.  */
361
		if (!clocksource_mips.read) {
L
Linus Torvalds 已提交
362
			/* No external high precision timer -- use R4k.  */
363
			clocksource_mips.read = c0_hpt_read;
L
Linus Torvalds 已提交
364

365
			if (!mips_timer_state) {
L
Linus Torvalds 已提交
366 367
				/* No external timer interrupt -- use R4k.  */
				mips_timer_ack = c0_timer_ack;
368 369 370 371 372 373 374 375
				/* Calculate cache parameters.  */
				cycles_per_jiffy =
					(mips_hpt_frequency + HZ / 2) / HZ;
				/*
				 * This sets up the high precision
				 * timer for the first interrupt.
				 */
				c0_hpt_timer_init();
L
Linus Torvalds 已提交
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
			}
		}
		if (!mips_hpt_frequency)
			mips_hpt_frequency = calibrate_hpt();

		/* Report the high precision timer rate for a reference.  */
		printk("Using %u.%03u MHz high precision timer.\n",
		       ((mips_hpt_frequency + 500) / 1000) / 1000,
		       ((mips_hpt_frequency + 500) / 1000) % 1000);
	}

	if (!mips_timer_ack)
		/* No timer interrupt ack (e.g. i8254).  */
		mips_timer_ack = null_timer_ack;

	/*
	 * Call board specific timer interrupt setup.
	 *
	 * this pointer must be setup in machine setup routine.
	 *
	 * Even if a machine chooses to use a low-level timer interrupt,
	 * it still needs to setup the timer_irqaction.
	 * In that case, it might be better to set timer_irqaction.handler
	 * to be NULL function so that we are sure the high-level code
	 * is not invoked accidentally.
	 */
402
	plat_timer_setup(&timer_irqaction);
403 404

	init_mips_clocksource();
L
Linus Torvalds 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
}

#define FEBRUARY		2
#define STARTOFTIME		1970
#define SECDAY			86400L
#define SECYR			(SECDAY * 365)
#define leapyear(y)		((!((y) % 4) && ((y) % 100)) || !((y) % 400))
#define days_in_year(y)		(leapyear(y) ? 366 : 365)
#define days_in_month(m)	(month_days[(m) - 1])

static int month_days[12] = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

void to_tm(unsigned long tim, struct rtc_time *tm)
{
	long hms, day, gday;
	int i;

	gday = day = tim / SECDAY;
	hms = tim % SECDAY;

	/* Hours, minutes, seconds are easy */
	tm->tm_hour = hms / 3600;
	tm->tm_min = (hms % 3600) / 60;
	tm->tm_sec = (hms % 3600) % 60;

	/* Number of years in days */
	for (i = STARTOFTIME; day >= days_in_year(i); i++)
		day -= days_in_year(i);
	tm->tm_year = i;

	/* Number of months in days left */
	if (leapyear(tm->tm_year))
		days_in_month(FEBRUARY) = 29;
	for (i = 1; day >= days_in_month(i); i++)
		day -= days_in_month(i);
	days_in_month(FEBRUARY) = 28;
	tm->tm_mon = i - 1;		/* tm_mon starts from 0 to 11 */

	/* Days are what is left over (+1) from all that. */
	tm->tm_mday = day + 1;

	/*
	 * Determine the day of week
	 */
	tm->tm_wday = (gday + 4) % 7;	/* 1970/1/1 was Thursday */
}

EXPORT_SYMBOL(rtc_lock);
EXPORT_SYMBOL(to_tm);
456 457
EXPORT_SYMBOL(rtc_mips_set_time);
EXPORT_SYMBOL(rtc_mips_get_time);