time.c 6.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * 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.
 */
14
#include <linux/clockchips.h>
L
Linus Torvalds 已提交
15 16 17 18 19
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/param.h>
20
#include <linux/profile.h>
L
Linus Torvalds 已提交
21 22 23 24 25 26 27
#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>
R
Ralf Baechle 已提交
28
#include <linux/kallsyms.h>
L
Linus Torvalds 已提交
29 30

#include <asm/bootinfo.h>
R
Ralf Baechle 已提交
31
#include <asm/cache.h>
L
Linus Torvalds 已提交
32 33 34 35 36
#include <asm/compiler.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
#include <asm/div64.h>
#include <asm/sections.h>
R
Ralf Baechle 已提交
37
#include <asm/smtc_ipi.h>
L
Linus Torvalds 已提交
38 39
#include <asm/time.h>

40 41
#include <irq.h>

L
Linus Torvalds 已提交
42 43 44 45
/*
 * forward reference
 */
DEFINE_SPINLOCK(rtc_lock);
46
EXPORT_SYMBOL(rtc_lock);
L
Linus Torvalds 已提交
47

48
int __weak rtc_mips_set_time(unsigned long sec)
L
Linus Torvalds 已提交
49
{
50
	return 0;
L
Linus Torvalds 已提交
51
}
52
EXPORT_SYMBOL(rtc_mips_set_time);
L
Linus Torvalds 已提交
53

54
int __weak rtc_mips_set_mmss(unsigned long nowtime)
L
Linus Torvalds 已提交
55
{
56
	return rtc_mips_set_time(nowtime);
L
Linus Torvalds 已提交
57 58
}

59 60 61 62
int update_persistent_clock(struct timespec now)
{
	return rtc_mips_set_mmss(now.tv_sec);
}
L
Linus Torvalds 已提交
63 64 65 66

/*
 * Null high precision timer functions for systems lacking one.
 */
67
static cycle_t null_hpt_read(void)
L
Linus Torvalds 已提交
68 69 70 71 72 73 74
{
	return 0;
}

/*
 * High precision timer functions for a R4k-compatible timer.
 */
75
static cycle_t c0_hpt_read(void)
L
Linus Torvalds 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
{
	return read_c0_count();
}

int (*mips_timer_state)(void);

/*
 * 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.
 */
92
void local_timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
93
{
94
	profile_tick(CPU_PROFILING);
95
	update_process_times(user_mode(get_irq_regs()));
L
Linus Torvalds 已提交
96 97
}

98
int null_perf_irq(void)
99 100 101 102
{
	return 0;
}

103 104
EXPORT_SYMBOL(null_perf_irq);

105
int (*perf_irq)(void) = null_perf_irq;
106 107 108

EXPORT_SYMBOL(perf_irq);

L
Linus Torvalds 已提交
109 110 111
/*
 * time_init() - it does the following things.
 *
112
 * 1) plat_time_init() -
L
Linus Torvalds 已提交
113 114
 * 	a) (optional) set up RTC routines,
 *      b) (optional) calibrate and set the mips_hpt_frequency
115 116
 *	    (only needed if you intended to use cpu counter as timer interrupt
 *	     source)
117 118
 * 2) calculate a couple of cached variables for later usage
 * 3) plat_timer_setup() -
L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127
 *	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
 */

unsigned int mips_hpt_frequency;

static unsigned int __init calibrate_hpt(void)
{
128
	cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

	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.
	 */
154
	hpt_start = clocksource_mips.read();
L
Linus Torvalds 已提交
155 156 157 158
	do {
		while (mips_timer_state());
		while (!mips_timer_state());
	} while (--i);
159
	hpt_end = clocksource_mips.read();
L
Linus Torvalds 已提交
160

161
	hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask;
L
Linus Torvalds 已提交
162
	hz = HZ;
163
	frequency = hpt_count * hz;
L
Linus Torvalds 已提交
164 165 166 167

	return frequency >> log_2_loops;
}

168
struct clocksource clocksource_mips = {
169
	.name		= "MIPS",
170
	.mask		= CLOCKSOURCE_MASK(32),
171
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
172 173
};

174
void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock)
175 176 177 178
{
	u64 temp;
	u32 shift;

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	/* Find a shift value */
	for (shift = 32; shift > 0; shift--) {
		temp = (u64) NSEC_PER_SEC << shift;
		do_div(temp, clock);
		if ((temp >> 32) == 0)
			break;
	}
	cs->shift = shift;
	cs->mult = (u32) temp;
}

void __cpuinit clockevent_set_clock(struct clock_event_device *cd,
	unsigned int clock)
{
	u64 temp;
	u32 shift;
195 196 197 198

	/* Find a shift value */
	for (shift = 32; shift > 0; shift--) {
		temp = (u64) NSEC_PER_SEC << shift;
199
		do_div(temp, clock);
200 201 202
		if ((temp >> 32) == 0)
			break;
	}
203 204 205 206 207 208 209 210 211 212 213 214 215
	cd->shift = shift;
	cd->mult = (u32) temp;
}

static void __init init_mips_clocksource(void)
{
	if (!mips_hpt_frequency || clocksource_mips.read == null_hpt_read)
		return;

	/* Calclate a somewhat reasonable rating value */
	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;

	clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
216 217 218 219

	clocksource_register(&clocksource_mips);
}

220
void __init __weak plat_time_init(void)
L
Linus Torvalds 已提交
221
{
222
}
L
Linus Torvalds 已提交
223

224 225 226 227
void __init __weak plat_timer_setup(struct irqaction *irq)
{
}

228 229 230
void __init time_init(void)
{
	plat_time_init();
L
Linus Torvalds 已提交
231 232

	/* Choose appropriate high precision timer routines.  */
233
	if (!cpu_has_counter && !clocksource_mips.read)
L
Linus Torvalds 已提交
234
		/* No high precision timer -- sorry.  */
235
		clocksource_mips.read = null_hpt_read;
236
	else if (!mips_hpt_frequency && !mips_timer_state) {
L
Linus Torvalds 已提交
237
		/* A high precision timer of unknown frequency.  */
238
		if (!clocksource_mips.read)
L
Linus Torvalds 已提交
239
			/* No external high precision timer -- use R4k.  */
240
			clocksource_mips.read = c0_hpt_read;
L
Linus Torvalds 已提交
241 242
	} else {
		/* We know counter frequency.  Or we can get it.  */
243
		if (!clocksource_mips.read) {
L
Linus Torvalds 已提交
244
			/* No external high precision timer -- use R4k.  */
245
			clocksource_mips.read = c0_hpt_read;
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255
		}
		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);
	}

256
	init_mips_clocksource();
257
	mips_clockevent_init();
L
Linus Torvalds 已提交
258
}