time_32.c 6.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 28 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
/*
 *  linux/arch/i386/kernel/time.c
 *
 *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
 *
 * This file contains the PC-specific time handling details:
 * reading the RTC at bootup, etc..
 * 1994-07-02    Alan Modra
 *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
 * 1995-03-26    Markus Kuhn
 *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
 *      precision CMOS clock update
 * 1996-05-03    Ingo Molnar
 *      fixed time warps in do_[slow|fast]_gettimeoffset()
 * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
 *		"A Kernel Model for Precision Timekeeping" by Dave Mills
 * 1998-09-05    (Various)
 *	More robust do_fast_gettimeoffset() algorithm implemented
 *	(works with APM, Cyrix 6x86MX and Centaur C6),
 *	monotonic gettimeofday() with fast_get_timeoffset(),
 *	drift-proof precision TSC calibration on boot
 *	(C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D.
 *	Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>;
 *	ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
 * 1998-12-16    Andrea Arcangeli
 *	Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy
 *	because was not accounting lost_ticks.
 * 1998-12-24 Copyright (C) 1998  Andrea Arcangeli
 *	Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
 *	serialize accesses to xtime/lost_ticks).
 */

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/bcd.h>
#include <linux/efi.h>
#include <linux/mca.h>

#include <asm/io.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/msr.h>
#include <asm/delay.h>
#include <asm/mpspec.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/timer.h>
59
#include <asm/time.h>
L
Linus Torvalds 已提交
60 61 62 63 64 65 66 67 68 69 70

#include "mach_time.h"

#include <linux/timex.h>

#include <asm/hpet.h>

#include <asm/arch_hooks.h>

#include "io_ports.h"

71 72
#include <asm/i8259.h>

L
Linus Torvalds 已提交
73 74
#include "do_timer.h"

A
Andrew Morton 已提交
75
unsigned int cpu_khz;	/* Detected as we calibrate the TSC */
76
EXPORT_SYMBOL(cpu_khz);
L
Linus Torvalds 已提交
77 78

DEFINE_SPINLOCK(rtc_lock);
79
EXPORT_SYMBOL(rtc_lock);
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

/*
 * This is a special lock that is owned by the CPU and holds the index
 * register we are working with.  It is required for NMI access to the
 * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
 */
volatile unsigned long cmos_lock = 0;
EXPORT_SYMBOL(cmos_lock);

/* Routines for accessing the CMOS RAM/RTC. */
unsigned char rtc_cmos_read(unsigned char addr)
{
	unsigned char val;
	lock_cmos_prefix(addr);
	outb_p(addr, RTC_PORT(0));
	val = inb_p(RTC_PORT(1));
	lock_cmos_suffix(addr);
	return val;
}
EXPORT_SYMBOL(rtc_cmos_read);

void rtc_cmos_write(unsigned char val, unsigned char addr)
{
	lock_cmos_prefix(addr);
	outb_p(addr, RTC_PORT(0));
	outb_p(val, RTC_PORT(1));
	lock_cmos_suffix(addr);
}
EXPORT_SYMBOL(rtc_cmos_write);

static int set_rtc_mmss(unsigned long nowtime)
{
	int retval;
113
	unsigned long flags;
L
Linus Torvalds 已提交
114 115

	/* gets recalled with irq locally disabled */
116 117
	/* XXX - does irqsave resolve this? -johnstul */
	spin_lock_irqsave(&rtc_lock, flags);
118
	retval = set_wallclock(nowtime);
119
	spin_unlock_irqrestore(&rtc_lock, flags);
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128 129 130

	return retval;
}


int timer_ack;

unsigned long profile_pc(struct pt_regs *regs)
{
	unsigned long pc = instruction_pointer(regs);

131
#ifdef CONFIG_SMP
132 133
	if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) &&
	    in_lock_functions(pc)) {
134
#ifdef CONFIG_FRAME_POINTER
L
Linus Torvalds 已提交
135
		return *(unsigned long *)(regs->ebp + 4);
136
#else
137 138
		unsigned long *sp = (unsigned long *)&regs->esp;

139 140 141 142 143 144 145 146 147 148
		/* Return address is either directly at stack pointer
		   or above a saved eflags. Eflags has bits 22-31 zero,
		   kernel addresses don't. */
 		if (sp[0] >> 22)
			return sp[0];
		if (sp[1] >> 22)
			return sp[1];
#endif
	}
#endif
L
Linus Torvalds 已提交
149 150 151 152 153
	return pc;
}
EXPORT_SYMBOL(profile_pc);

/*
154 155 156
 * This is the same as the above, except we _also_ save the current
 * Time Stamp Counter value at the time of the timer interrupt, so that
 * we later on can estimate the time of day more exactly.
L
Linus Torvalds 已提交
157
 */
158
irqreturn_t timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
159
{
160 161 162
	/* Keep nmi watchdog up to date */
	per_cpu(irq_stat, smp_processor_id()).irq0_irqs++;

L
Linus Torvalds 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
#ifdef CONFIG_X86_IO_APIC
	if (timer_ack) {
		/*
		 * Subtle, when I/O APICs are used we have to ack timer IRQ
		 * manually to reset the IRR bit for do_slow_gettimeoffset().
		 * This will also deassert NMI lines for the watchdog if run
		 * on an 82489DX-based system.
		 */
		spin_lock(&i8259A_lock);
		outb(0x0c, PIC_MASTER_OCW3);
		/* Ack the IRQ; AEOI will end it automatically. */
		inb(PIC_MASTER_POLL);
		spin_unlock(&i8259A_lock);
	}
#endif

179
	do_timer_interrupt_hook();
L
Linus Torvalds 已提交
180 181 182 183 184 185 186 187 188 189 190

	if (MCA_bus) {
		/* The PS/2 uses level-triggered interrupts.  You can't
		turn them off, nor would you want to (any attempt to
		enable edge-triggered interrupts usually gets intercepted by a
		special hardware circuit).  Hence we have to acknowledge
		the timer interrupt.  Through some incredibly stupid
		design idea, the reset for IRQ 0 is done by setting the
		high bit of the PPI port B (0x61).  Note that some PS/2s,
		notably the 55SX, work fine if this is removed.  */

191 192
		u8 irq_v = inb_p( 0x61 );	/* read the current state */
		outb_p( irq_v|0x80, 0x61 );	/* reset the IRQ */
L
Linus Torvalds 已提交
193 194 195 196 197 198
	}

	return IRQ_HANDLED;
}

/* not static: needed by APM */
199
unsigned long read_persistent_clock(void)
L
Linus Torvalds 已提交
200 201
{
	unsigned long retval;
202
	unsigned long flags;
L
Linus Torvalds 已提交
203

204
	spin_lock_irqsave(&rtc_lock, flags);
L
Linus Torvalds 已提交
205

206
	retval = get_wallclock();
L
Linus Torvalds 已提交
207

208
	spin_unlock_irqrestore(&rtc_lock, flags);
L
Linus Torvalds 已提交
209 210 211

	return retval;
}
212

213
int update_persistent_clock(struct timespec now)
L
Linus Torvalds 已提交
214
{
215
	return set_rtc_mmss(now.tv_sec);
L
Linus Torvalds 已提交
216 217
}

218
extern void (*late_time_init)(void);
L
Linus Torvalds 已提交
219
/* Duplicate of time_init() below, with hpet_enable part added */
Z
Zachary Amsden 已提交
220
void __init hpet_time_init(void)
L
Linus Torvalds 已提交
221
{
222 223
	if (!hpet_enable())
		setup_pit_timer();
Z
Zachary Amsden 已提交
224
	time_init_hook();
L
Linus Torvalds 已提交
225 226
}

Z
Zachary Amsden 已提交
227 228 229 230 231 232 233 234
/*
 * This is called directly from init code; we must delay timer setup in the
 * HPET case as we can't make the decision to turn on HPET this early in the
 * boot process.
 *
 * The chosen time_init function will usually be hpet_time_init, above, but
 * in the case of virtual hardware, an alternative function may be substituted.
 */
L
Linus Torvalds 已提交
235 236
void __init time_init(void)
{
237
	tsc_init();
Z
Zachary Amsden 已提交
238
	late_time_init = choose_time_init();
L
Linus Torvalds 已提交
239
}