time.c 4.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * Copyright 2001 MontaVista Software Inc.
 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
 * Copyright (c) 2003, 2004  Maciej W. Rozycki
 *
A
Atsushi Nemoto 已提交
6
 * Common time service routines for MIPS machines.
L
Linus Torvalds 已提交
7 8 9 10 11 12
 *
 * 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.
 */
13
#include <linux/bug.h>
14
#include <linux/clockchips.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27
#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/spinlock.h>
#include <linux/module.h>

#include <asm/cpu-features.h>
#include <asm/div64.h>
R
Ralf Baechle 已提交
28
#include <asm/smtc_ipi.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34
#include <asm/time.h>

/*
 * forward reference
 */
DEFINE_SPINLOCK(rtc_lock);
35
EXPORT_SYMBOL(rtc_lock);
L
Linus Torvalds 已提交
36

37
int __weak rtc_mips_set_time(unsigned long sec)
L
Linus Torvalds 已提交
38
{
39
	return 0;
L
Linus Torvalds 已提交
40
}
41
EXPORT_SYMBOL(rtc_mips_set_time);
L
Linus Torvalds 已提交
42

43
int __weak rtc_mips_set_mmss(unsigned long nowtime)
L
Linus Torvalds 已提交
44
{
45
	return rtc_mips_set_time(nowtime);
L
Linus Torvalds 已提交
46 47
}

48 49 50 51
int update_persistent_clock(struct timespec now)
{
	return rtc_mips_set_mmss(now.tv_sec);
}
L
Linus Torvalds 已提交
52 53 54 55

/*
 * High precision timer functions for a R4k-compatible timer.
 */
56
static cycle_t c0_hpt_read(void)
L
Linus Torvalds 已提交
57 58 59 60 61 62
{
	return read_c0_count();
}

int (*mips_timer_state)(void);

63
int null_perf_irq(void)
64 65 66 67
{
	return 0;
}

68 69
EXPORT_SYMBOL(null_perf_irq);

70
int (*perf_irq)(void) = null_perf_irq;
71 72 73

EXPORT_SYMBOL(perf_irq);

L
Linus Torvalds 已提交
74 75 76
/*
 * time_init() - it does the following things.
 *
77
 * 1) plat_time_init() -
L
Linus Torvalds 已提交
78 79
 * 	a) (optional) set up RTC routines,
 *      b) (optional) calibrate and set the mips_hpt_frequency
80 81
 *	    (only needed if you intended to use cpu counter as timer interrupt
 *	     source)
82
 * 2) calculate a couple of cached variables for later usage
L
Linus Torvalds 已提交
83 84 85 86
 */

unsigned int mips_hpt_frequency;

A
Atsushi Nemoto 已提交
87 88 89 90 91 92 93
static struct clocksource clocksource_mips = {
	.name		= "MIPS",
	.read		= c0_hpt_read,
	.mask		= CLOCKSOURCE_MASK(32),
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};

L
Linus Torvalds 已提交
94 95
static unsigned int __init calibrate_hpt(void)
{
96
	cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

	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.
	 */
122
	hpt_start = clocksource_mips.read();
L
Linus Torvalds 已提交
123 124 125 126
	do {
		while (mips_timer_state());
		while (!mips_timer_state());
	} while (--i);
127
	hpt_end = clocksource_mips.read();
L
Linus Torvalds 已提交
128

129
	hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask;
L
Linus Torvalds 已提交
130
	hz = HZ;
131
	frequency = hpt_count * hz;
L
Linus Torvalds 已提交
132 133 134 135

	return frequency >> log_2_loops;
}

136
void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock)
137 138 139 140
{
	u64 temp;
	u32 shift;

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	/* 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;
157 158 159

	/* Find a shift value */
	for (shift = 32; shift > 0; shift--) {
160 161
		temp = (u64) clock << shift;
		do_div(temp, NSEC_PER_SEC);
162 163 164
		if ((temp >> 32) == 0)
			break;
	}
165 166 167 168 169 170 171 172 173 174
	cd->shift = shift;
	cd->mult = (u32) temp;
}

static void __init init_mips_clocksource(void)
{
	/* Calclate a somewhat reasonable rating value */
	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;

	clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
175 176 177 178

	clocksource_register(&clocksource_mips);
}

179
void __init __weak plat_time_init(void)
L
Linus Torvalds 已提交
180
{
181
}
L
Linus Torvalds 已提交
182

183 184 185 186 187 188 189 190 191
/*
 * This function exists in order to cause an error due to a duplicate
 * definition if platform code should have its own implementation.  The hook
 * to use instead is plat_time_init.  plat_time_init does not receive the
 * irqaction pointer argument anymore.  This is because any function which
 * initializes an interrupt timer now takes care of its own request_irq rsp.
 * setup_irq calls and each clock_event_device should use its own
 * struct irqrequest.
 */
A
Atsushi Nemoto 已提交
192
void __init plat_timer_setup(void)
193
{
194
	BUG();
195 196
}

197 198 199
void __init time_init(void)
{
	plat_time_init();
L
Linus Torvalds 已提交
200

A
Atsushi Nemoto 已提交
201
	if (cpu_has_counter && (mips_hpt_frequency || mips_timer_state)) {
L
Linus Torvalds 已提交
202 203 204 205 206 207 208 209
		/* We know counter frequency.  Or we can get it.  */
		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);
A
Atsushi Nemoto 已提交
210
		init_mips_clocksource();
L
Linus Torvalds 已提交
211 212
	}

213
	mips_clockevent_init();
L
Linus Torvalds 已提交
214
}